2 /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.com)
5 This file is part of groff.
7 groff is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
12 groff is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 You should have received a copy of the GNU General Public License along
18 with groff; see the file COPYING. If not, write to the Free Software
19 Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
23 #define BAR_HEIGHT ".25m"
24 #define DOUBLE_LINE_SEP "2p"
25 #define HALF_DOUBLE_LINE_SEP "1p"
27 #define BODY_DEPTH ".25m"
29 const int DEFAULT_COLUMN_SEPARATION
= 3;
31 #define DELIMITER_CHAR "\\[tbl]"
33 #define SEPARATION_FACTOR_REG PREFIX "sep"
34 #define BOTTOM_REG PREFIX "bot"
35 #define RESET_MACRO_NAME PREFIX "init"
36 #define LINESIZE_REG PREFIX "lps"
37 #define TOP_REG PREFIX "top"
38 #define CURRENT_ROW_REG PREFIX "crow"
39 #define LAST_PASSED_ROW_REG PREFIX "passed"
40 #define TRANSPARENT_STRING_NAME PREFIX "trans"
41 #define QUOTE_STRING_NAME PREFIX "quote"
42 #define SECTION_DIVERSION_NAME PREFIX "section"
43 #define SECTION_DIVERSION_FLAG_REG PREFIX "sflag"
44 #define SAVED_VERTICAL_POS_REG PREFIX "vert"
45 #define NEED_BOTTOM_RULE_REG PREFIX "brule"
46 #define KEEP_MACRO_NAME PREFIX "keep"
47 #define RELEASE_MACRO_NAME PREFIX "release"
48 #define SAVED_FONT_REG PREFIX "fnt"
49 #define SAVED_SIZE_REG PREFIX "sz"
50 #define SAVED_FILL_REG PREFIX "fll"
51 #define SAVED_INDENT_REG PREFIX "ind"
52 #define SAVED_CENTER_REG PREFIX "cent"
53 #define TABLE_DIVERSION_NAME PREFIX "table"
54 #define TABLE_DIVERSION_FLAG_REG PREFIX "tflag"
55 #define TABLE_KEEP_MACRO_NAME PREFIX "tkeep"
56 #define TABLE_RELEASE_MACRO_NAME PREFIX "trelease"
57 #define NEEDED_REG PREFIX "needed"
58 #define REPEATED_MARK_MACRO PREFIX "rmk"
59 #define REPEATED_VPT_MACRO PREFIX "rvpt"
60 #define SUPPRESS_BOTTOM_REG PREFIX "supbot"
61 #define SAVED_DN_REG PREFIX "dn"
63 // this must be one character
64 #define COMPATIBLE_REG PREFIX "c"
66 #define BLOCK_WIDTH_PREFIX PREFIX "tbw"
67 #define BLOCK_DIVERSION_PREFIX PREFIX "tbd"
68 #define BLOCK_HEIGHT_PREFIX PREFIX "tbh"
69 #define SPAN_WIDTH_PREFIX PREFIX "w"
70 #define SPAN_LEFT_NUMERIC_WIDTH_PREFIX PREFIX "lnw"
71 #define SPAN_RIGHT_NUMERIC_WIDTH_PREFIX PREFIX "rnw"
72 #define SPAN_ALPHABETIC_WIDTH_PREFIX PREFIX "aw"
73 #define COLUMN_SEPARATION_PREFIX PREFIX "cs"
74 #define ROW_START_PREFIX PREFIX "rs"
75 #define COLUMN_START_PREFIX PREFIX "cl"
76 #define COLUMN_END_PREFIX PREFIX "ce"
77 #define COLUMN_DIVIDE_PREFIX PREFIX "cd"
78 #define ROW_TOP_PREFIX PREFIX "rt"
80 string
block_width_reg(int r
, int c
);
81 string
block_diversion_name(int r
, int c
);
82 string
block_height_reg(int r
, int c
);
83 string
span_width_reg(int start_col
, int end_col
);
84 string
span_left_numeric_width_reg(int start_col
, int end_col
);
85 string
span_right_numeric_width_reg(int start_col
, int end_col
);
86 string
span_alphabetic_width_reg(int start_col
, int end_col
);
87 string
column_separation_reg(int col
);
88 string
row_start_reg(int r
);
89 string
column_start_reg(int c
);
90 string
column_end_reg(int c
);
91 string
column_divide_reg(int c
);
92 string
row_top_reg(int r
);
94 void set_inline_modifier(const entry_modifier
*);
95 void restore_inline_modifier(const entry_modifier
*m
);
96 void set_modifier(const entry_modifier
*);
97 int find_dot(const char *s
, const char *delim
);
99 string an_empty_string
;
100 int location_force_filename
= 0;
102 void printfs(const char *,
103 const string
&arg1
= an_empty_string
,
104 const string
&arg2
= an_empty_string
,
105 const string
&arg3
= an_empty_string
,
106 const string
&arg4
= an_empty_string
,
107 const string
&arg5
= an_empty_string
);
109 void prints(const string
&);
111 inline void prints(char c
)
116 inline void prints(const char *s
)
121 void prints(const string
&s
)
124 fwrite(s
.contents(), 1, s
.length(), stdout
);
127 struct horizontal_span
{
128 horizontal_span
*next
;
131 horizontal_span(int, int, horizontal_span
*);
134 struct single_line_entry
;
135 struct double_line_entry
;
142 const char *input_filename
;
148 const entry_modifier
*mod
;
151 table_entry(const entry_modifier
*);
152 virtual ~table_entry();
153 virtual int divert(int ncols
, const string
*mw
, int *sep
);
154 virtual void do_width();
155 virtual void do_depth();
156 virtual void print() = 0;
157 virtual void position_vertically() = 0;
158 virtual single_line_entry
*to_single_line_entry();
159 virtual double_line_entry
*to_double_line_entry();
160 virtual simple_entry
*to_simple_entry();
161 virtual int line_type();
162 virtual void note_double_vrule_on_right(int);
163 virtual void note_double_vrule_on_left(int);
166 class simple_entry
: public table_entry
{
168 simple_entry(const entry_modifier
*);
170 void position_vertically();
171 simple_entry
*to_simple_entry();
172 virtual void add_tab();
173 virtual void simple_print(int);
176 class empty_entry
: public simple_entry
{
178 empty_entry(const entry_modifier
*);
182 class text_entry
: public simple_entry
{
185 void print_contents();
187 text_entry(char *, const entry_modifier
*);
191 void text_entry::print_contents()
193 set_inline_modifier(mod
);
195 restore_inline_modifier(mod
);
198 class repeated_char_entry
: public text_entry
{
200 repeated_char_entry(char *s
, const entry_modifier
*m
);
201 void simple_print(int);
204 class simple_text_entry
: public text_entry
{
206 simple_text_entry(char *s
, const entry_modifier
*m
);
210 class left_text_entry
: public simple_text_entry
{
212 left_text_entry(char *s
, const entry_modifier
*m
);
213 void simple_print(int);
217 class right_text_entry
: public simple_text_entry
{
219 right_text_entry(char *s
, const entry_modifier
*m
);
220 void simple_print(int);
224 class center_text_entry
: public simple_text_entry
{
226 center_text_entry(char *s
, const entry_modifier
*m
);
227 void simple_print(int);
231 class numeric_text_entry
: public text_entry
{
234 numeric_text_entry(char *s
, const entry_modifier
*m
, int pos
);
236 void simple_print(int);
239 class alphabetic_text_entry
: public text_entry
{
241 alphabetic_text_entry(char *s
, const entry_modifier
*m
);
243 void simple_print(int);
247 class line_entry
: public simple_entry
{
249 char double_vrule_on_right
;
250 char double_vrule_on_left
;
252 line_entry(const entry_modifier
*);
253 void note_double_vrule_on_right(int);
254 void note_double_vrule_on_left(int);
255 void simple_print(int) = 0;
258 class single_line_entry
: public line_entry
{
260 single_line_entry(const entry_modifier
*m
);
261 void simple_print(int);
262 single_line_entry
*to_single_line_entry();
266 class double_line_entry
: public line_entry
{
268 double_line_entry(const entry_modifier
*m
);
269 void simple_print(int);
270 double_line_entry
*to_double_line_entry();
274 class short_line_entry
: public simple_entry
{
276 short_line_entry(const entry_modifier
*m
);
277 void simple_print(int);
281 class short_double_line_entry
: public simple_entry
{
283 short_double_line_entry(const entry_modifier
*m
);
284 void simple_print(int);
288 class block_entry
: public table_entry
{
291 void do_divert(int alphabetic
, int ncols
, const string
*mw
, int *sep
);
293 block_entry(char *s
, const entry_modifier
*m
);
295 int divert(int ncols
, const string
*mw
, int *sep
);
298 void position_vertically();
302 class left_block_entry
: public block_entry
{
304 left_block_entry(char *s
, const entry_modifier
*m
);
308 class right_block_entry
: public block_entry
{
310 right_block_entry(char *s
, const entry_modifier
*m
);
314 class center_block_entry
: public block_entry
{
316 center_block_entry(char *s
, const entry_modifier
*m
);
320 class alphabetic_block_entry
: public block_entry
{
322 alphabetic_block_entry(char *s
, const entry_modifier
*m
);
324 int divert(int ncols
, const string
*mw
, int *sep
);
327 table_entry::table_entry(const entry_modifier
*m
)
328 : next(0), start_row(-1), end_row(-1), start_col(-1), end_col(-1), mod(m
),
329 input_lineno(-1), input_filename(0)
333 table_entry::~table_entry()
337 int table_entry::divert(int, const string
*, int *)
342 void table_entry::do_width()
346 single_line_entry
*table_entry::to_single_line_entry()
351 double_line_entry
*table_entry::to_double_line_entry()
356 simple_entry
*table_entry::to_simple_entry()
361 void table_entry::do_depth()
365 void table_entry::set_location()
367 set_troff_location(input_filename
, input_lineno
);
370 int table_entry::line_type()
375 void table_entry::note_double_vrule_on_right(int)
379 void table_entry::note_double_vrule_on_left(int)
383 simple_entry::simple_entry(const entry_modifier
*m
) : table_entry(m
)
387 void simple_entry::add_tab()
392 void simple_entry::simple_print(int)
397 void simple_entry::position_vertically()
399 if (start_row
!= end_row
)
400 switch (mod
->vertical_alignment
) {
401 case entry_modifier::TOP
:
402 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row
));
404 case entry_modifier::CENTER
:
405 // Peform the motion in two stages so that the center is rounded
406 // vertically upwards even if net vertical motion is upwards.
407 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row
));
408 printfs(".sp \\n[" BOTTOM_REG
"]u-\\n[%1]u-1v/2u\n",
409 row_start_reg(start_row
));
411 case entry_modifier::BOTTOM
:
412 printfs(".sp |\\n[%1]u+\\n[" BOTTOM_REG
"]u-\\n[%1]u-1v\n",
413 row_start_reg(start_row
));
420 void simple_entry::print()
431 simple_entry
*simple_entry::to_simple_entry()
436 empty_entry::empty_entry(const entry_modifier
*m
)
441 int empty_entry::line_type()
446 text_entry::text_entry(char *s
, const entry_modifier
*m
)
447 : contents(s
), simple_entry(m
)
451 text_entry::~text_entry()
457 repeated_char_entry::repeated_char_entry(char *s
, const entry_modifier
*m
)
462 void repeated_char_entry::simple_print(int)
464 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col
));
465 set_inline_modifier(mod
);
466 printfs("\\l" DELIMITER_CHAR
"\\n[%1]u\\&",
467 span_width_reg(start_col
, end_col
));
469 prints(DELIMITER_CHAR
);
470 restore_inline_modifier(mod
);
473 simple_text_entry::simple_text_entry(char *s
, const entry_modifier
*m
)
478 void simple_text_entry::do_width()
481 printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR
,
482 span_width_reg(start_col
, end_col
));
484 prints(DELIMITER_CHAR
"\n");
487 left_text_entry::left_text_entry(char *s
, const entry_modifier
*m
)
488 : simple_text_entry(s
, m
)
492 void left_text_entry::simple_print(int)
494 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col
));
498 // The only point of this is to make `\a' ``work'' as in Unix tbl. Grrr.
500 void left_text_entry::add_tab()
502 printfs(" \\n[%1]u", column_end_reg(end_col
));
505 right_text_entry::right_text_entry(char *s
, const entry_modifier
*m
)
506 : simple_text_entry(s
, m
)
510 void right_text_entry::simple_print(int)
512 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col
));
518 void right_text_entry::add_tab()
520 printfs(" \\n[%1]u", column_end_reg(end_col
));
523 center_text_entry::center_text_entry(char *s
, const entry_modifier
*m
)
524 : simple_text_entry(s
, m
)
528 void center_text_entry::simple_print(int)
530 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col
));
536 void center_text_entry::add_tab()
538 printfs(" \\n[%1]u", column_end_reg(end_col
));
541 numeric_text_entry::numeric_text_entry(char *s
, const entry_modifier
*m
, int pos
)
542 : text_entry(s
, m
), dot_pos(pos
)
546 void numeric_text_entry::do_width()
550 printfs(".nr %1 0\\w" DELIMITER_CHAR
,
551 block_width_reg(start_row
, start_col
));
552 set_inline_modifier(mod
);
553 for (int i
= 0; i
< dot_pos
; i
++)
555 restore_inline_modifier(mod
);
556 prints(DELIMITER_CHAR
"\n");
557 printfs(".nr %1 \\n[%1]>?\\n[%2]\n",
558 span_left_numeric_width_reg(start_col
, end_col
),
559 block_width_reg(start_row
, start_col
));
562 printfs(".nr %1 0\n", block_width_reg(start_row
, start_col
));
563 if (contents
[dot_pos
] != '\0') {
565 printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR
,
566 span_right_numeric_width_reg(start_col
, end_col
));
567 set_inline_modifier(mod
);
568 prints(contents
+ dot_pos
);
569 restore_inline_modifier(mod
);
570 prints(DELIMITER_CHAR
"\n");
574 void numeric_text_entry::simple_print(int)
576 printfs("\\h'|(\\n[%1]u-\\n[%2]u-\\n[%3]u/2u+\\n[%2]u+\\n[%4]u-\\n[%5]u)'",
577 span_width_reg(start_col
, end_col
),
578 span_left_numeric_width_reg(start_col
, end_col
),
579 span_right_numeric_width_reg(start_col
, end_col
),
580 column_start_reg(start_col
),
581 block_width_reg(start_row
, start_col
));
585 alphabetic_text_entry::alphabetic_text_entry(char *s
, const entry_modifier
*m
)
590 void alphabetic_text_entry::do_width()
593 printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR
,
594 span_alphabetic_width_reg(start_col
, end_col
));
596 prints(DELIMITER_CHAR
"\n");
599 void alphabetic_text_entry::simple_print(int)
601 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col
));
602 printfs("\\h'\\n[%1]u-\\n[%2]u/2u'",
603 span_width_reg(start_col
, end_col
),
604 span_alphabetic_width_reg(start_col
, end_col
));
608 // The only point of this is to make `\a' ``work'' as in Unix tbl. Grrr.
610 void alphabetic_text_entry::add_tab()
612 printfs(" \\n[%1]u", column_end_reg(end_col
));
615 block_entry::block_entry(char *s
, const entry_modifier
*m
)
616 : table_entry(m
), contents(s
)
620 block_entry::~block_entry()
625 void block_entry::position_vertically()
627 if (start_row
!= end_row
)
628 switch(mod
->vertical_alignment
) {
629 case entry_modifier::TOP
:
630 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row
));
632 case entry_modifier::CENTER
:
633 // Peform the motion in two stages so that the center is rounded
634 // vertically upwards even if net vertical motion is upwards.
635 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row
));
636 printfs(".sp \\n[" BOTTOM_REG
"]u-\\n[%1]u-\\n[%2]u/2u\n",
637 row_start_reg(start_row
),
638 block_height_reg(start_row
, start_col
));
640 case entry_modifier::BOTTOM
:
641 printfs(".sp |\\n[%1]u+\\n[" BOTTOM_REG
"]u-\\n[%1]u-\\n[%2]u\n",
642 row_start_reg(start_row
),
643 block_height_reg(start_row
, start_col
));
649 prints(".sp -.5v\n");
652 int block_entry::divert(int ncols
, const string
*mw
, int *sep
)
654 do_divert(0, ncols
, mw
, sep
);
658 void block_entry::do_divert(int alphabetic
, int ncols
, const string
*mw
,
661 printfs(".di %1\n", block_diversion_name(start_row
, start_col
));
662 prints(".if \\n[" SAVED_FILL_REG
"] .fi\n"
665 for (int i
= start_col
; i
<= end_col
; i
++)
669 // Every column spanned by this entry has a minimum width.
670 for (int i
= start_col
; i
<= end_col
; i
++) {
673 printfs("+%1n", as_string(sep
[i
- 1]));
676 printfs("(n;%1)", mw
[i
]);
678 printfs(">?\\n[%1]u", span_width_reg(start_col
, end_col
));
681 printfs("(u;\\n[%1]>?(\\n[.l]*%2/%3))",
682 span_width_reg(start_col
, end_col
),
683 as_string(end_col
- start_col
+ 1),
684 as_string(ncols
+ 1));
689 prints(".cp \\n(" COMPATIBLE_REG
"\n");
692 prints(".br\n.di\n.cp 0\n");
693 if (!mod
->zero_width
) {
695 printfs(".nr %1 \\n[%1]>?(\\n[dl]+2n)\n",
696 span_width_reg(start_col
, end_col
));
697 printfs(".nr %1 \\n[%1]>?\\n[dl]\n",
698 span_alphabetic_width_reg(start_col
, end_col
));
701 printfs(".nr %1 \\n[%1]>?\\n[dl]\n", span_width_reg(start_col
, end_col
));
703 printfs(".nr %1 \\n[dn]\n", block_height_reg(start_row
, start_col
));
704 printfs(".nr %1 \\n[dl]\n", block_width_reg(start_row
, start_col
));
705 prints("." RESET_MACRO_NAME
"\n"
706 ".in \\n[" SAVED_INDENT_REG
"]u\n"
708 // the block might have contained .lf commands
709 location_force_filename
= 1;
712 void block_entry::do_width()
714 // do nothing; the action happens in divert
717 void block_entry::do_depth()
719 printfs(".nr " BOTTOM_REG
" \\n[" BOTTOM_REG
"]>?(\\n[%1]+\\n[%2])\n",
720 row_start_reg(start_row
),
721 block_height_reg(start_row
, start_col
));
724 left_block_entry::left_block_entry(char *s
, const entry_modifier
*m
)
729 void left_block_entry::print()
731 printfs(".in +\\n[%1]u\n", column_start_reg(start_col
));
732 printfs(".%1\n", block_diversion_name(start_row
, start_col
));
738 right_block_entry::right_block_entry(char *s
, const entry_modifier
*m
)
743 void right_block_entry::print()
745 printfs(".in +\\n[%1]u+\\n[%2]u-\\n[%3]u\n",
746 column_start_reg(start_col
),
747 span_width_reg(start_col
, end_col
),
748 block_width_reg(start_row
, start_col
));
749 printfs(".%1\n", block_diversion_name(start_row
, start_col
));
753 center_block_entry::center_block_entry(char *s
, const entry_modifier
*m
)
758 void center_block_entry::print()
760 printfs(".in +\\n[%1]u+(\\n[%2]u-\\n[%3]u/2u)\n",
761 column_start_reg(start_col
),
762 span_width_reg(start_col
, end_col
),
763 block_width_reg(start_row
, start_col
));
764 printfs(".%1\n", block_diversion_name(start_row
, start_col
));
768 alphabetic_block_entry::alphabetic_block_entry(char *s
,
769 const entry_modifier
*m
)
774 int alphabetic_block_entry::divert(int ncols
, const string
*mw
, int *sep
)
776 do_divert(1, ncols
, mw
, sep
);
780 void alphabetic_block_entry::print()
782 printfs(".in +\\n[%1]u+(\\n[%2]u-\\n[%3]u/2u)\n",
783 column_start_reg(start_col
),
784 span_width_reg(start_col
, end_col
),
785 span_alphabetic_width_reg(start_col
, end_col
));
786 printfs(".%1\n", block_diversion_name(start_row
, start_col
));
790 line_entry::line_entry(const entry_modifier
*m
)
791 : simple_entry(m
), double_vrule_on_right(0), double_vrule_on_left(0)
795 void line_entry::note_double_vrule_on_right(int is_corner
)
797 double_vrule_on_right
= is_corner
? 1 : 2;
800 void line_entry::note_double_vrule_on_left(int is_corner
)
802 double_vrule_on_left
= is_corner
? 1 : 2;
806 single_line_entry::single_line_entry(const entry_modifier
*m
)
811 int single_line_entry::line_type()
816 void single_line_entry::simple_print(int dont_move
)
818 printfs("\\h'|\\n[%1]u",
819 column_divide_reg(start_col
));
820 if (double_vrule_on_left
) {
821 prints(double_vrule_on_left
== 1 ? "-" : "+");
822 prints(HALF_DOUBLE_LINE_SEP
);
826 prints("\\v'-" BAR_HEIGHT
"'");
827 printfs("\\s[\\n[" LINESIZE_REG
"]]" "\\D'l |\\n[%1]u",
828 column_divide_reg(end_col
+1));
829 if (double_vrule_on_right
) {
830 prints(double_vrule_on_left
== 1 ? "+" : "-");
831 prints(HALF_DOUBLE_LINE_SEP
);
835 prints("\\v'" BAR_HEIGHT
"'");
838 single_line_entry
*single_line_entry::to_single_line_entry()
843 double_line_entry::double_line_entry(const entry_modifier
*m
)
848 int double_line_entry::line_type()
853 void double_line_entry::simple_print(int dont_move
)
856 prints("\\v'-" BAR_HEIGHT
"'");
857 printfs("\\h'|\\n[%1]u",
858 column_divide_reg(start_col
));
859 if (double_vrule_on_left
) {
860 prints(double_vrule_on_left
== 1 ? "-" : "+");
861 prints(HALF_DOUBLE_LINE_SEP
);
864 printfs("\\v'-" HALF_DOUBLE_LINE_SEP
"'"
865 "\\s[\\n[" LINESIZE_REG
"]]"
867 column_divide_reg(end_col
+1));
868 if (double_vrule_on_right
)
869 prints("-" HALF_DOUBLE_LINE_SEP
);
871 printfs("\\v'" DOUBLE_LINE_SEP
"'"
873 column_divide_reg(start_col
));
874 if (double_vrule_on_right
) {
875 prints(double_vrule_on_left
== 1 ? "+" : "-");
876 prints(HALF_DOUBLE_LINE_SEP
);
880 "\\v'-" HALF_DOUBLE_LINE_SEP
"'");
882 prints("\\v'" BAR_HEIGHT
"'");
885 double_line_entry
*double_line_entry::to_double_line_entry()
890 short_line_entry::short_line_entry(const entry_modifier
*m
)
895 int short_line_entry::line_type()
900 void short_line_entry::simple_print(int dont_move
)
905 prints("\\v'-" BAR_HEIGHT
"'");
906 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col
));
907 printfs("\\s[\\n[" LINESIZE_REG
"]]"
910 span_width_reg(start_col
, end_col
));
912 prints("\\v'" BAR_HEIGHT
"'");
917 short_double_line_entry::short_double_line_entry(const entry_modifier
*m
)
922 int short_double_line_entry::line_type()
927 void short_double_line_entry::simple_print(int dont_move
)
932 prints("\\v'-" BAR_HEIGHT
"'");
933 printfs("\\h'|\\n[%2]u'"
934 "\\v'-" HALF_DOUBLE_LINE_SEP
"'"
935 "\\s[\\n[" LINESIZE_REG
"]]"
937 "\\v'" DOUBLE_LINE_SEP
"'"
940 "\\v'-" HALF_DOUBLE_LINE_SEP
"'",
941 span_width_reg(start_col
, end_col
),
942 column_start_reg(start_col
));
944 prints("\\v'" BAR_HEIGHT
"'");
949 void set_modifier(const entry_modifier
*m
)
951 if (!m
->font
.empty())
952 printfs(".ft %1\n", m
->font
);
953 if (m
->point_size
.val
!= 0) {
955 if (m
->point_size
.inc
> 0)
957 else if (m
->point_size
.inc
< 0)
959 printfs("%1\n", as_string(m
->point_size
.val
));
961 if (m
->vertical_spacing
.val
!= 0) {
963 if (m
->vertical_spacing
.inc
> 0)
965 else if (m
->vertical_spacing
.inc
< 0)
967 printfs("%1\n", as_string(m
->vertical_spacing
.val
));
971 void set_inline_modifier(const entry_modifier
*m
)
973 if (!m
->font
.empty())
974 printfs("\\f[%1]", m
->font
);
975 if (m
->point_size
.val
!= 0) {
977 if (m
->point_size
.inc
> 0)
979 else if (m
->point_size
.inc
< 0)
981 printfs("%1]", as_string(m
->point_size
.val
));
987 void restore_inline_modifier(const entry_modifier
*m
)
989 if (!m
->font
.empty())
990 prints("\\f[\\n[" SAVED_FONT_REG
"]]");
991 if (m
->point_size
.val
!= 0)
992 prints("\\s[\\n[" SAVED_SIZE_REG
"]]");
1000 int row
; // occurs before row `row'
1001 char printed
; // has it been printed?
1004 virtual void print(table
*) = 0;
1006 virtual int is_single_line() { return 0; };
1007 virtual int is_double_line() { return 0; };
1010 stuff::stuff(int r
) : row(r
), next(0), printed(0)
1018 struct text_stuff
: stuff
{
1020 const char *filename
;
1023 text_stuff(const string
&, int r
, const char *fn
, int ln
);
1025 void print(table
*);
1029 text_stuff::text_stuff(const string
&s
, int r
, const char *fn
, int ln
)
1030 : contents(s
), stuff(r
), filename(fn
), lineno(ln
)
1034 text_stuff::~text_stuff()
1038 void text_stuff::print(table
*)
1041 prints(".cp \\n(" COMPATIBLE_REG
"\n");
1042 set_troff_location(filename
, lineno
);
1045 location_force_filename
= 1; // it might have been a .lf command
1048 struct single_hline_stuff
: stuff
{
1049 single_hline_stuff(int r
);
1050 void print(table
*);
1051 int is_single_line();
1054 single_hline_stuff::single_hline_stuff(int r
) : stuff(r
)
1058 void single_hline_stuff::print(table
*tbl
)
1061 tbl
->print_single_hline(row
);
1064 int single_hline_stuff::is_single_line()
1069 struct double_hline_stuff
: stuff
{
1070 double_hline_stuff(int r
);
1071 void print(table
*);
1072 int is_double_line();
1075 double_hline_stuff::double_hline_stuff(int r
) : stuff(r
)
1079 void double_hline_stuff::print(table
*tbl
)
1082 tbl
->print_double_hline(row
);
1085 int double_hline_stuff::is_double_line()
1090 struct vertical_rule
{
1091 vertical_rule
*next
;
1099 vertical_rule(int sr
, int er
, int c
, int dbl
, vertical_rule
*);
1101 void contribute_to_bottom_macro(table
*);
1105 vertical_rule::vertical_rule(int sr
, int er
, int c
, int dbl
, vertical_rule
*p
)
1106 : start_row(sr
), end_row(er
), col(c
), is_double(dbl
), next(p
)
1110 vertical_rule::~vertical_rule()
1114 void vertical_rule::contribute_to_bottom_macro(table
*tbl
)
1116 printfs(".if \\n[" CURRENT_ROW_REG
"]>=%1",
1117 as_string(start_row
));
1118 if (end_row
!= tbl
->get_nrows() - 1)
1119 printfs("&(\\n[" CURRENT_ROW_REG
"]<%1)",
1120 as_string(end_row
));
1122 printfs(".if %1<=\\n[" LAST_PASSED_ROW_REG
"] .nr %2 \\n[#T]\n",
1123 as_string(start_row
),
1124 row_top_reg(start_row
));
1125 const char *offset_table
[3];
1127 offset_table
[0] = "-" HALF_DOUBLE_LINE_SEP
;
1128 offset_table
[1] = "+" HALF_DOUBLE_LINE_SEP
;
1129 offset_table
[2] = 0;
1132 offset_table
[0] = "";
1133 offset_table
[1] = 0;
1135 for (const char **offsetp
= offset_table
; *offsetp
; offsetp
++) {
1138 if (!bot_adjust
.empty())
1139 printfs("+%1", bot_adjust
);
1141 printfs("\\h'\\n[%1]u%3'\\s[\\n[" LINESIZE_REG
"]]\\D'l 0 |\\n[%2]u-1v",
1142 column_divide_reg(col
),
1143 row_top_reg(start_row
),
1145 if (!bot_adjust
.empty())
1146 printfs("-(%1)", bot_adjust
);
1147 // don't perform the top adjustment if the top is actually #T
1148 if (!top_adjust
.empty())
1149 printfs("+((%1)*(%2>\\n[" LAST_PASSED_ROW_REG
"]))",
1151 as_string(start_row
));
1157 void vertical_rule::print()
1159 printfs("\\*[" TRANSPARENT_STRING_NAME
"]"
1160 ".if %1<=\\*[" QUOTE_STRING_NAME
"]\\n[" LAST_PASSED_ROW_REG
"] "
1161 ".nr %2 \\*[" QUOTE_STRING_NAME
"]\\n[#T]\n",
1162 as_string(start_row
),
1163 row_top_reg(start_row
));
1164 const char *offset_table
[3];
1166 offset_table
[0] = "-" HALF_DOUBLE_LINE_SEP
;
1167 offset_table
[1] = "+" HALF_DOUBLE_LINE_SEP
;
1168 offset_table
[2] = 0;
1171 offset_table
[0] = "";
1172 offset_table
[1] = 0;
1174 for (const char **offsetp
= offset_table
; *offsetp
; offsetp
++) {
1175 prints("\\*[" TRANSPARENT_STRING_NAME
"].sp -1\n"
1176 "\\*[" TRANSPARENT_STRING_NAME
"]\\v'" BODY_DEPTH
);
1177 if (!bot_adjust
.empty())
1178 printfs("+%1", bot_adjust
);
1180 printfs("\\h'\\n[%1]u%3'"
1181 "\\s[\\n[" LINESIZE_REG
"]]"
1182 "\\D'l 0 |\\*[" QUOTE_STRING_NAME
"]\\n[%2]u-1v",
1183 column_divide_reg(col
),
1184 row_top_reg(start_row
),
1186 if (!bot_adjust
.empty())
1187 printfs("-(%1)", bot_adjust
);
1188 // don't perform the top adjustment if the top is actually #T
1189 if (!top_adjust
.empty())
1190 printfs("+((%1)*(%2>\\*[" QUOTE_STRING_NAME
"]\\n["
1191 LAST_PASSED_ROW_REG
"]))",
1193 as_string(start_row
));
1199 table::table(int nc
, unsigned f
, int ls
)
1200 : ncolumns(nc
), flags(f
), linesize(ls
),
1201 nrows(0), allocated_rows(0), entry(0), entry_list(0),
1202 left_separation(0), right_separation(0), stuff_list(0), vline(0),
1203 vrule_list(0), row_is_all_lines(0), span_list(0)
1205 minimum_width
= new string
[ncolumns
];
1206 column_separation
= ncolumns
> 1 ? new int[ncolumns
- 1] : 0;
1207 equal
= new char[ncolumns
];
1209 for (i
= 0; i
< ncolumns
; i
++)
1211 for (i
= 0; i
< ncolumns
-1; i
++)
1212 column_separation
[i
] = DEFAULT_COLUMN_SEPARATION
;
1213 delim
[0] = delim
[1] = '\0';
1218 for (int i
= 0; i
< nrows
; i
++) {
1224 while (entry_list
) {
1225 table_entry
*tem
= entry_list
;
1226 entry_list
= entry_list
->next
;
1229 ad_delete(ncolumns
) minimum_width
;
1230 a_delete column_separation
;
1232 while (stuff_list
) {
1233 stuff
*tem
= stuff_list
;
1234 stuff_list
= stuff_list
->next
;
1237 while (vrule_list
) {
1238 vertical_rule
*tem
= vrule_list
;
1239 vrule_list
= vrule_list
->next
;
1242 a_delete row_is_all_lines
;
1244 horizontal_span
*tem
= span_list
;
1245 span_list
= span_list
->next
;
1250 void table::set_delim(char c1
, char c2
)
1256 void table::set_minimum_width(int c
, const string
&w
)
1258 assert(c
>= 0 && c
< ncolumns
);
1259 minimum_width
[c
] = w
;
1262 void table::set_column_separation(int c
, int n
)
1264 assert(c
>= 0 && c
< ncolumns
- 1);
1265 column_separation
[c
] = n
;
1268 void table::set_equal_column(int c
)
1270 assert(c
>= 0 && c
< ncolumns
);
1274 void table::add_stuff(stuff
*p
)
1276 for (stuff
**pp
= &stuff_list
; *pp
; pp
= &(*pp
)->next
)
1281 void table::add_text_line(int r
, const string
&s
, const char *filename
, int lineno
)
1283 add_stuff(new text_stuff(s
, r
, filename
, lineno
));
1286 void table::add_single_hline(int r
)
1288 add_stuff(new single_hline_stuff(r
));
1291 void table::add_double_hline(int r
)
1293 add_stuff(new double_hline_stuff(r
));
1296 void table::allocate(int r
)
1299 typedef table_entry
**PPtable_entry
; // work around g++ 1.36.1 bug
1300 if (r
>= allocated_rows
) {
1301 if (allocated_rows
== 0) {
1302 allocated_rows
= 16;
1303 if (allocated_rows
<= r
)
1304 allocated_rows
= r
+ 1;
1305 entry
= new PPtable_entry
[allocated_rows
];
1306 vline
= new char*[allocated_rows
];
1309 table_entry
***old_entry
= entry
;
1310 int old_allocated_rows
= allocated_rows
;
1311 allocated_rows
*= 2;
1312 if (allocated_rows
<= r
)
1313 allocated_rows
= r
+ 1;
1314 entry
= new PPtable_entry
[allocated_rows
];
1315 memcpy(entry
, old_entry
, sizeof(table_entry
**)*old_allocated_rows
);
1317 char **old_vline
= vline
;
1318 vline
= new char*[allocated_rows
];
1319 memcpy(vline
, old_vline
, sizeof(char*)*old_allocated_rows
);
1323 assert(allocated_rows
> r
);
1324 while (nrows
<= r
) {
1325 entry
[nrows
] = new table_entry
*[ncolumns
];
1326 for (int i
= 0; i
< ncolumns
; i
++)
1327 entry
[nrows
][i
] = 0;
1328 vline
[nrows
] = new char[ncolumns
+1];
1329 for (i
= 0; i
< ncolumns
+1; i
++)
1330 vline
[nrows
][i
] = 0;
1336 void table::do_hspan(int r
, int c
)
1338 assert(r
>= 0 && c
>= 0 && r
< nrows
&& c
< ncolumns
);
1340 error("first column cannot be horizontally spanned");
1343 table_entry
*e
= entry
[r
][c
];
1345 assert(e
->start_row
<= r
&& r
<= e
->end_row
1346 && e
->start_col
<= c
&& c
<= e
->end_col
1347 && e
->end_row
- e
->start_row
> 0
1348 && e
->end_col
- e
->start_col
> 0);
1352 // e can be 0 if we had an empty entry or an error
1355 if (e
->start_row
!= r
) {
1359 error("impossible horizontal span at row %1, column %2", r
+ 1, c
+ 1);
1367 void table::do_vspan(int r
, int c
)
1369 assert(r
>= 0 && c
>= 0 && r
< nrows
&& c
< ncolumns
);
1371 error("first row cannot be vertically spanned");
1374 table_entry
*e
= entry
[r
][c
];
1376 assert(e
->start_row
<= r
&& r
<= e
->end_row
1377 && e
->start_col
<= c
&& c
<= e
->end_col
1378 && e
->end_row
- e
->start_row
> 0
1379 && e
->end_col
- e
->start_col
> 0);
1383 // e can be 0 if we had an empty entry or an error
1386 if (e
->start_col
!= c
) {
1389 error("impossible vertical span at row %1, column %2", r
+ 1, c
+ 1);
1392 for (int i
= c
; i
<= e
->end_col
; i
++) {
1393 assert(entry
[r
][i
] == 0);
1400 int find_dot(const char *s
, const char *delim
)
1402 if (s
== 0 || *s
== '\0')
1405 int in_delim
= 0; // is p within eqn delimiters?
1406 // tbl recognises \& even within eqn delimiters; I don't
1407 for (p
= s
; *p
; p
++)
1412 else if (*p
== delim
[0])
1414 else if (p
[0] == '\\' && p
[1] == '&')
1416 int possible_pos
= -1;
1418 for (p
= s
; *p
; p
++)
1423 else if (*p
== delim
[0])
1425 else if (p
[0] == '.' && csdigit(p
[1]))
1426 possible_pos
= p
- s
;
1427 if (possible_pos
>= 0)
1428 return possible_pos
;
1430 for (p
= s
; *p
; p
++)
1435 else if (*p
== delim
[0])
1437 else if (csdigit(*p
))
1438 possible_pos
= p
+ 1 - s
;
1439 return possible_pos
;
1442 void table::add_entry(int r
, int c
, const string
&str
, const entry_format
*f
,
1443 const char *fn
, int ln
)
1448 e
= new short_line_entry(f
);
1450 else if (str
== "\\=") {
1451 e
= new short_double_line_entry(f
);
1453 else if (str
== "_") {
1454 single_line_entry
*lefte
;
1455 if (c
> 0 && entry
[r
][c
-1] != 0 &&
1456 (lefte
= entry
[r
][c
-1]->to_single_line_entry()) != 0
1457 && lefte
->start_row
== r
1458 && lefte
->mod
->stagger
== f
->stagger
) {
1460 entry
[r
][c
] = lefte
;
1463 e
= new single_line_entry(f
);
1465 else if (str
== "=") {
1466 double_line_entry
*lefte
;
1467 if (c
> 0 && entry
[r
][c
-1] != 0 &&
1468 (lefte
= entry
[r
][c
-1]->to_double_line_entry()) != 0
1469 && lefte
->start_row
== r
1470 && lefte
->mod
->stagger
== f
->stagger
) {
1472 entry
[r
][c
] = lefte
;
1475 e
= new double_line_entry(f
);
1477 else if (str
== "\\^") {
1480 else if (str
.length() > 2 && str
[0] == '\\' && str
[1] == 'R') {
1481 if (str
.search('\n') >= 0)
1482 error_with_file_and_line(fn
, ln
, "bad repeated character");
1484 char *s
= str
.substring(2, str
.length() - 2).extract();
1485 e
= new repeated_char_entry(s
, f
);
1489 int is_block
= str
.search('\n') >= 0;
1493 assert(str
.empty());
1500 e
= new left_block_entry(s
, f
);
1502 e
= new left_text_entry(s
, f
);
1505 e
= new empty_entry(f
);
1511 e
= new center_block_entry(s
, f
);
1513 e
= new center_text_entry(s
, f
);
1516 e
= new empty_entry(f
);
1522 e
= new right_block_entry(s
, f
);
1524 e
= new right_text_entry(s
, f
);
1527 e
= new empty_entry(f
);
1529 case FORMAT_NUMERIC
:
1533 error_with_file_and_line(fn
, ln
, "can't have numeric text block");
1534 e
= new left_block_entry(s
, f
);
1537 int pos
= find_dot(s
, delim
);
1539 e
= new center_text_entry(s
, f
);
1541 e
= new numeric_text_entry(s
, f
, pos
);
1545 e
= new empty_entry(f
);
1547 case FORMAT_ALPHABETIC
:
1551 e
= new alphabetic_block_entry(s
, f
);
1553 e
= new alphabetic_text_entry(s
, f
);
1556 e
= new empty_entry(f
);
1562 if (str
.length() != 0)
1563 error_with_file_and_line(fn
, ln
,
1564 "non-empty data entry for `_' format ignored");
1565 e
= new single_line_entry(f
);
1567 case FORMAT_DOUBLE_HLINE
:
1568 if (str
.length() != 0)
1569 error_with_file_and_line(fn
, ln
,
1570 "non-empty data entry for `=' format ignored");
1571 e
= new double_line_entry(f
);
1578 table_entry
*preve
= entry
[r
][c
];
1582 error_with_file_and_line(fn
, ln
, "row %1, column %2 already spanned",
1587 e
->input_lineno
= ln
;
1588 e
->input_filename
= fn
;
1589 e
->start_row
= e
->end_row
= r
;
1590 e
->start_col
= e
->end_col
= c
;
1591 for (table_entry
**p
= &entry_list
; *p
; p
= &(*p
)->next
)
1599 // add vertical lines for row r
1601 void table::add_vlines(int r
, const char *v
)
1604 for (int i
= 0; i
< ncolumns
+1; i
++)
1610 table_entry
*p
= entry_list
;
1613 for (i
= p
->start_row
; i
<= p
->end_row
; i
++)
1614 for (j
= p
->start_col
; j
<= p
->end_col
; j
++)
1615 assert(entry
[i
][j
] == p
);
1622 location_force_filename
= 1;
1625 determine_row_type();
1627 if (!(flags
& CENTER
))
1628 prints(".if \\n[" SAVED_CENTER_REG
"] \\{");
1629 prints(".in +(u;\\n[.l]-\\n[.i]-\\n[TW]/2)\n"
1630 ".nr " SAVED_INDENT_REG
" \\n[.i]\n");
1631 if (!(flags
& CENTER
))
1634 define_bottom_macro();
1636 for (int i
= 0; i
< nrows
; i
++)
1641 void table::determine_row_type()
1643 row_is_all_lines
= new char[nrows
];
1644 for (int i
= 0; i
< nrows
; i
++) {
1647 int had_non_line
= 0;
1648 for (int c
= 0; c
< ncolumns
; c
++) {
1649 table_entry
*e
= entry
[i
][c
];
1651 if (e
->start_row
== e
->end_row
) {
1652 int t
= e
->line_type();
1676 row_is_all_lines
[i
] = 0;
1677 else if (had_double
)
1678 row_is_all_lines
[i
] = 2;
1679 else if (had_single
)
1680 row_is_all_lines
[i
] = 1;
1682 row_is_all_lines
[i
] = 0;
1687 void table::init_output()
1689 prints(".nr " COMPATIBLE_REG
" \\n(.C\n"
1692 printfs(".nr " LINESIZE_REG
" %1\n", as_string(linesize
));
1694 prints(".nr " LINESIZE_REG
" \\n[.s]\n");
1695 if (!(flags
& CENTER
))
1696 prints(".nr " SAVED_CENTER_REG
" \\n[.ce]\n");
1697 prints(".de " RESET_MACRO_NAME
"\n"
1709 ".nr " SAVED_INDENT_REG
" \\n[.i]\n"
1710 ".nr " SAVED_FONT_REG
" \\n[.f]\n"
1711 ".nr " SAVED_SIZE_REG
" \\n[.s]\n"
1712 ".nr " SAVED_FILL_REG
" \\n[.u]\n"
1714 ".nr " CURRENT_ROW_REG
" 0-1\n"
1715 ".nr " LAST_PASSED_ROW_REG
" 0-1\n"
1716 ".nr " SECTION_DIVERSION_FLAG_REG
" 0\n"
1717 ".ds " TRANSPARENT_STRING_NAME
"\n"
1718 ".ds " QUOTE_STRING_NAME
"\n"
1719 ".nr " NEED_BOTTOM_RULE_REG
" 1\n"
1720 ".nr " SUPPRESS_BOTTOM_REG
" 0\n"
1722 ".de " REPEATED_MARK_MACRO
"\n"
1724 ".if !'\\n(.z'' \\!." REPEATED_MARK_MACRO
" \"\\$1\"\n"
1726 ".de " REPEATED_VPT_MACRO
"\n"
1728 ".if !'\\n(.z'' \\!." REPEATED_VPT_MACRO
" \"\\$1\"\n"
1730 if (!(flags
& NOKEEP
))
1731 prints(".de " KEEP_MACRO_NAME
"\n"
1732 ".if '\\n[.z]'' \\{.ds " QUOTE_STRING_NAME
" \\\\\n"
1733 ".ds " TRANSPARENT_STRING_NAME
" \\!\n"
1734 ".di " SECTION_DIVERSION_NAME
"\n"
1735 ".nr " SECTION_DIVERSION_FLAG_REG
" 1\n"
1739 ".de " RELEASE_MACRO_NAME
"\n"
1740 ".if \\n[" SECTION_DIVERSION_FLAG_REG
"] \\{"
1742 ".in \\n[" SAVED_INDENT_REG
"]u\n"
1743 ".nr " SAVED_DN_REG
" \\n[dn]\n"
1744 ".ds " QUOTE_STRING_NAME
"\n"
1745 ".ds " TRANSPARENT_STRING_NAME
"\n"
1746 ".nr " SECTION_DIVERSION_FLAG_REG
" 0\n"
1747 ".if \\n[.t]<=\\n[dn] \\{"
1750 ".nr " SUPPRESS_BOTTOM_REG
" 1\n"
1752 ".nr " SUPPRESS_BOTTOM_REG
" 0\n"
1755 ".if \\n[.t]<=\\n[" SAVED_DN_REG
"] "
1756 /* Since we turn off traps, it won't get into an infinite loop
1757 when we try and print it; it will just go off the bottom of the
1759 ".tm warning: page \\n%: table text block will not fit on one page\n"
1762 "." SECTION_DIVERSION_NAME
"\n"
1764 ".rm " SECTION_DIVERSION_NAME
"\n"
1767 ".nr " TABLE_DIVERSION_FLAG_REG
" 0\n"
1768 ".de " TABLE_KEEP_MACRO_NAME
"\n"
1769 ".if '\\n[.z]'' \\{"
1770 ".di " TABLE_DIVERSION_NAME
"\n"
1771 ".nr " TABLE_DIVERSION_FLAG_REG
" 1\n"
1774 ".de " TABLE_RELEASE_MACRO_NAME
"\n"
1775 ".if \\n[" TABLE_DIVERSION_FLAG_REG
"] \\{.br\n"
1777 ".nr " SAVED_DN_REG
" \\n[dn]\n"
1778 ".ne \\n[dn]u+\\n[.V]u\n"
1779 ".ie \\n[.t]<=\\n[" SAVED_DN_REG
"] "
1780 ".tm error: page \\n%: table will not fit on one page; use .TS H/.TH with a supporting macro package\n"
1785 "." TABLE_DIVERSION_NAME
"\n"
1787 ".rm " TABLE_DIVERSION_NAME
"\n"
1795 string
block_width_reg(int r
, int c
)
1797 static char name
[sizeof(BLOCK_WIDTH_PREFIX
)+INT_DIGITS
+1+INT_DIGITS
];
1798 sprintf(name
, BLOCK_WIDTH_PREFIX
"%d,%d", r
, c
);
1799 return string(name
);
1802 string
block_diversion_name(int r
, int c
)
1804 static char name
[sizeof(BLOCK_DIVERSION_PREFIX
)+INT_DIGITS
+1+INT_DIGITS
];
1805 sprintf(name
, BLOCK_DIVERSION_PREFIX
"%d,%d", r
, c
);
1806 return string(name
);
1809 string
block_height_reg(int r
, int c
)
1811 static char name
[sizeof(BLOCK_HEIGHT_PREFIX
)+INT_DIGITS
+1+INT_DIGITS
];
1812 sprintf(name
, BLOCK_HEIGHT_PREFIX
"%d,%d", r
, c
);
1813 return string(name
);
1816 string
span_width_reg(int start_col
, int end_col
)
1818 static char name
[sizeof(SPAN_WIDTH_PREFIX
)+INT_DIGITS
+1+INT_DIGITS
];
1819 sprintf(name
, SPAN_WIDTH_PREFIX
"%d", start_col
);
1820 if (end_col
!= start_col
)
1821 sprintf(strchr(name
, '\0'), ",%d", end_col
);
1822 return string(name
);
1825 string
span_left_numeric_width_reg(int start_col
, int end_col
)
1827 static char name
[sizeof(SPAN_LEFT_NUMERIC_WIDTH_PREFIX
)+INT_DIGITS
+1+INT_DIGITS
];
1828 sprintf(name
, SPAN_LEFT_NUMERIC_WIDTH_PREFIX
"%d", start_col
);
1829 if (end_col
!= start_col
)
1830 sprintf(strchr(name
, '\0'), ",%d", end_col
);
1831 return string(name
);
1834 string
span_right_numeric_width_reg(int start_col
, int end_col
)
1836 static char name
[sizeof(SPAN_RIGHT_NUMERIC_WIDTH_PREFIX
)+INT_DIGITS
+1+INT_DIGITS
];
1837 sprintf(name
, SPAN_RIGHT_NUMERIC_WIDTH_PREFIX
"%d", start_col
);
1838 if (end_col
!= start_col
)
1839 sprintf(strchr(name
, '\0'), ",%d", end_col
);
1840 return string(name
);
1843 string
span_alphabetic_width_reg(int start_col
, int end_col
)
1845 static char name
[sizeof(SPAN_ALPHABETIC_WIDTH_PREFIX
)+INT_DIGITS
+1+INT_DIGITS
];
1846 sprintf(name
, SPAN_ALPHABETIC_WIDTH_PREFIX
"%d", start_col
);
1847 if (end_col
!= start_col
)
1848 sprintf(strchr(name
, '\0'), ",%d", end_col
);
1849 return string(name
);
1853 string
column_separation_reg(int col
)
1855 static char name
[sizeof(COLUMN_SEPARATION_PREFIX
)+INT_DIGITS
];
1856 sprintf(name
, COLUMN_SEPARATION_PREFIX
"%d", col
);
1857 return string(name
);
1860 string
row_start_reg(int row
)
1862 static char name
[sizeof(ROW_START_PREFIX
)+INT_DIGITS
];
1863 sprintf(name
, ROW_START_PREFIX
"%d", row
);
1864 return string(name
);
1867 string
column_start_reg(int col
)
1869 static char name
[sizeof(COLUMN_START_PREFIX
)+INT_DIGITS
];
1870 sprintf(name
, COLUMN_START_PREFIX
"%d", col
);
1871 return string(name
);
1874 string
column_end_reg(int col
)
1876 static char name
[sizeof(COLUMN_END_PREFIX
)+INT_DIGITS
];
1877 sprintf(name
, COLUMN_END_PREFIX
"%d", col
);
1878 return string(name
);
1881 string
column_divide_reg(int col
)
1883 static char name
[sizeof(COLUMN_DIVIDE_PREFIX
)+INT_DIGITS
];
1884 sprintf(name
, COLUMN_DIVIDE_PREFIX
"%d", col
);
1885 return string(name
);
1888 string
row_top_reg(int row
)
1890 static char name
[sizeof(ROW_TOP_PREFIX
)+INT_DIGITS
];
1891 sprintf(name
, ROW_TOP_PREFIX
"%d", row
);
1892 return string(name
);
1895 void init_span_reg(int start_col
, int end_col
)
1897 printfs(".nr %1 \\n(.H\n.nr %2 0\n.nr %3 0\n.nr %4 0\n",
1898 span_width_reg(start_col
, end_col
),
1899 span_alphabetic_width_reg(start_col
, end_col
),
1900 span_left_numeric_width_reg(start_col
, end_col
),
1901 span_right_numeric_width_reg(start_col
, end_col
));
1904 void compute_span_width(int start_col
, int end_col
)
1906 printfs(".nr %1 \\n[%1]>?(\\n[%2]+\\n[%3])\n"
1907 ".if \\n[%4] .nr %1 \\n[%1]>?(\\n[%4]+2n)\n",
1908 span_width_reg(start_col
, end_col
),
1909 span_left_numeric_width_reg(start_col
, end_col
),
1910 span_right_numeric_width_reg(start_col
, end_col
),
1911 span_alphabetic_width_reg(start_col
, end_col
));
1915 // Increase the widths of columns so that the width of any spanning entry
1916 // is no greater than the sum of the widths of the columns that it spans.
1917 // Ensure that the widths of columns remain equal.
1919 void table::divide_span(int start_col
, int end_col
)
1921 assert(end_col
> start_col
);
1922 printfs(".nr " NEEDED_REG
" \\n[%1]-(\\n[%2]",
1923 span_width_reg(start_col
, end_col
),
1924 span_width_reg(start_col
, start_col
));
1925 for (int i
= start_col
+ 1; i
<= end_col
; i
++) {
1926 // The column separation may shrink with the expand option.
1927 if (!(flags
& EXPAND
))
1928 printfs("+%1n", as_string(column_separation
[i
- 1]));
1929 printfs("+\\n[%1]", span_width_reg(i
, i
));
1932 printfs(".nr " NEEDED_REG
" \\n[" NEEDED_REG
"]/%1\n",
1933 as_string(end_col
- start_col
+ 1));
1934 prints(".if \\n[" NEEDED_REG
"] \\{");
1935 for (i
= start_col
; i
<= end_col
; i
++)
1936 printfs(".nr %1 +\\n[" NEEDED_REG
"]\n",
1937 span_width_reg(i
, i
));
1939 for (i
= start_col
; i
<= end_col
&& !equal_flag
; i
++)
1943 for (i
= 0; i
< ncolumns
; i
++)
1944 if (i
< start_col
|| i
> end_col
)
1945 printfs(".nr %1 +\\n[" NEEDED_REG
"]\n",
1946 span_width_reg(i
, i
));
1952 void table::sum_columns(int start_col
, int end_col
)
1954 assert(end_col
> start_col
);
1955 printfs(".nr %1 \\n[%2]",
1956 span_width_reg(start_col
, end_col
),
1957 span_width_reg(start_col
, start_col
));
1958 for (int i
= start_col
+ 1; i
<= end_col
; i
++)
1959 printfs("+(%1*\\n[" SEPARATION_FACTOR_REG
"])+\\n[%2]",
1960 as_string(column_separation
[i
- 1]),
1961 span_width_reg(i
, i
));
1965 horizontal_span::horizontal_span(int sc
, int ec
, horizontal_span
*p
)
1966 : start_col(sc
), end_col(ec
), next(p
)
1970 void table::build_span_list()
1973 table_entry
*p
= entry_list
;
1975 if (p
->end_col
!= p
->start_col
) {
1976 for (horizontal_span
*q
= span_list
; q
; q
= q
->next
)
1977 if (q
->start_col
== p
->start_col
1978 && q
->end_col
== p
->end_col
)
1981 span_list
= new horizontal_span(p
->start_col
, p
->end_col
, span_list
);
1985 // Now sort span_list primarily by order of end_row, and secondarily
1986 // by reverse order of start_row. This ensures that if we divide
1987 // spans using the order in span_list, we will get reasonable results.
1988 horizontal_span
*unsorted
= span_list
;
1991 for (horizontal_span
**pp
= &span_list
; *pp
; pp
= &(*pp
)->next
)
1992 if (unsorted
->end_col
< (*pp
)->end_col
1993 || (unsorted
->end_col
== (*pp
)->end_col
1994 && (unsorted
->start_col
> (*pp
)->start_col
)))
1996 horizontal_span
*tem
= unsorted
->next
;
1997 unsorted
->next
= *pp
;
2004 void table::compute_separation_factor()
2006 if (flags
& (ALLBOX
|BOX
|DOUBLEBOX
))
2007 left_separation
= right_separation
= 1;
2009 for (int i
= 0; i
< nrows
; i
++) {
2010 if (vline
[i
][0] > 0)
2011 left_separation
= 1;
2012 if (vline
[i
][ncolumns
] > 0)
2013 right_separation
= 1;
2016 if (flags
& EXPAND
) {
2017 int total_sep
= left_separation
+ right_separation
;
2018 for (int i
= 0; i
< ncolumns
- 1; i
++)
2019 total_sep
+= column_separation
[i
];
2020 if (total_sep
!= 0) {
2021 // Don't let the separation factor be negative.
2022 prints(".nr " SEPARATION_FACTOR_REG
" \\n[.l]-\\n[.i]");
2023 for (i
= 0; i
< ncolumns
; i
++)
2024 printfs("-\\n[%1]", span_width_reg(i
, i
));
2025 printfs("/%1>?0\n", as_string(total_sep
));
2030 void table::compute_column_positions()
2032 printfs(".nr %1 0\n", column_divide_reg(0));
2033 printfs(".nr %1 %2*\\n[" SEPARATION_FACTOR_REG
"]\n",
2034 column_start_reg(0),
2035 as_string(left_separation
));
2036 for (int i
= 1;; i
++) {
2037 printfs(".nr %1 \\n[%2]+\\n[%3]\n",
2038 column_end_reg(i
-1),
2039 column_start_reg(i
-1),
2040 span_width_reg(i
-1, i
-1));
2043 printfs(".nr %1 \\n[%2]+(%3*\\n[" SEPARATION_FACTOR_REG
"])\n",
2044 column_start_reg(i
),
2045 column_end_reg(i
-1),
2046 as_string(column_separation
[i
-1]));
2047 printfs(".nr %1 \\n[%2]+\\n[%3]/2\n",
2048 column_divide_reg(i
),
2049 column_end_reg(i
-1),
2050 column_start_reg(i
));
2052 printfs(".nr %1 \\n[%2]+(%3*\\n[" SEPARATION_FACTOR_REG
"])\n",
2053 column_divide_reg(ncolumns
),
2054 column_end_reg(i
-1),
2055 as_string(right_separation
));
2056 printfs(".nr TW \\n[%1]\n",
2057 column_divide_reg(ncolumns
));
2058 if (flags
& DOUBLEBOX
) {
2059 printfs(".nr %1 +" DOUBLE_LINE_SEP
"\n", column_divide_reg(0));
2060 printfs(".nr %1 -" DOUBLE_LINE_SEP
"\n", column_divide_reg(ncolumns
));
2064 void table::make_columns_equal()
2066 int first
= -1; // index of first equal column
2067 for (int i
= 0; i
< ncolumns
; i
++)
2070 printfs(".nr %1 \\n[%1]", span_width_reg(i
, i
));
2074 printfs(">?\\n[%1]", span_width_reg(i
, i
));
2078 for (i
= first
+ 1; i
< ncolumns
; i
++)
2080 printfs(".nr %1 \\n[%2]\n",
2081 span_width_reg(i
, i
),
2082 span_width_reg(first
, first
));
2086 void table::compute_widths()
2091 prints(".nr " SEPARATION_FACTOR_REG
" 1n\n");
2092 for (i
= 0; i
< ncolumns
; i
++) {
2093 init_span_reg(i
, i
);
2094 if (!minimum_width
[i
].empty())
2095 printfs(".nr %1 %2\n", span_width_reg(i
, i
), minimum_width
[i
]);
2097 for (p
= span_list
; p
; p
= p
->next
)
2098 init_span_reg(p
->start_col
, p
->end_col
);
2100 for (q
= entry_list
; q
; q
= q
->next
)
2101 if (!q
->mod
->zero_width
)
2103 for (i
= 0; i
< ncolumns
; i
++)
2104 compute_span_width(i
, i
);
2105 for (p
= span_list
; p
; p
= p
->next
)
2106 compute_span_width(p
->start_col
, p
->end_col
);
2107 make_columns_equal();
2108 // Note that divide_span keeps equal width columns equal.
2109 for (p
= span_list
; p
; p
= p
->next
)
2110 divide_span(p
->start_col
, p
->end_col
);
2111 for (p
= span_list
; p
; p
= p
->next
)
2112 sum_columns(p
->start_col
, p
->end_col
);
2113 int had_spanning_block
= 0;
2114 int had_equal_block
= 0;
2115 for (q
= entry_list
; q
; q
= q
->next
)
2116 if (q
->divert(ncolumns
, minimum_width
,
2117 (flags
& EXPAND
) ? column_separation
: 0)) {
2118 if (q
->end_col
> q
->start_col
)
2119 had_spanning_block
= 1;
2120 for (i
= q
->start_col
; i
<= q
->end_col
&& !had_equal_block
; i
++)
2122 had_equal_block
= 1;
2124 if (had_equal_block
)
2125 make_columns_equal();
2126 if (had_spanning_block
)
2127 for (p
= span_list
; p
; p
= p
->next
)
2128 divide_span(p
->start_col
, p
->end_col
);
2129 compute_separation_factor();
2130 for (p
= span_list
; p
; p
= p
->next
)
2131 sum_columns(p
->start_col
, p
->end_col
);
2132 compute_column_positions();
2135 void table::print_single_hline(int r
)
2137 prints(".vs " LINE_SEP
">?\\n[.V]u\n"
2139 "\\v'" BODY_DEPTH
"'"
2140 "\\s[\\n[" LINESIZE_REG
"]]");
2142 prints("\\D'l |\\n[TW]u 0'");
2146 while (start_col
< ncolumns
2147 && entry
[r
][start_col
] != 0
2148 && entry
[r
][start_col
]->start_row
!= r
)
2150 for (int end_col
= start_col
;
2152 && (entry
[r
][end_col
] == 0
2153 || entry
[r
][end_col
]->start_row
== r
);
2156 if (end_col
<= start_col
)
2158 printfs("\\h'|\\n[%1]u",
2159 column_divide_reg(start_col
));
2160 if ((r
> 0 && vline
[r
-1][start_col
] == 2)
2161 || (r
< nrows
&& vline
[r
][start_col
] == 2))
2162 prints("-" HALF_DOUBLE_LINE_SEP
);
2164 printfs("\\D'l |\\n[%1]u",
2165 column_divide_reg(end_col
));
2166 if ((r
> 0 && vline
[r
-1][end_col
] == 2)
2167 || (r
< nrows
&& vline
[r
][end_col
] == 2))
2168 prints("+" HALF_DOUBLE_LINE_SEP
);
2170 start_col
= end_col
;
2178 void table::print_double_hline(int r
)
2180 prints(".vs " LINE_SEP
"+" DOUBLE_LINE_SEP
2183 "\\v'" BODY_DEPTH
"'"
2184 "\\s[\\n[" LINESIZE_REG
"]]");
2186 prints("\\v'-" DOUBLE_LINE_SEP
"'"
2187 "\\D'l |\\n[TW]u 0'"
2188 "\\v'" DOUBLE_LINE_SEP
"'"
2190 "\\D'l |\\n[TW]u 0'");
2194 while (start_col
< ncolumns
2195 && entry
[r
][start_col
] != 0
2196 && entry
[r
][start_col
]->start_row
!= r
)
2198 for (int end_col
= start_col
;
2200 && (entry
[r
][end_col
] == 0
2201 || entry
[r
][end_col
]->start_row
== r
);
2204 if (end_col
<= start_col
)
2206 const char *left_adjust
= 0;
2207 if ((r
> 0 && vline
[r
-1][start_col
] == 2)
2208 || (r
< nrows
&& vline
[r
][start_col
] == 2))
2209 left_adjust
= "-" HALF_DOUBLE_LINE_SEP
;
2210 const char *right_adjust
= 0;
2211 if ((r
> 0 && vline
[r
-1][end_col
] == 2)
2212 || (r
< nrows
&& vline
[r
][end_col
] == 2))
2213 right_adjust
= "+" HALF_DOUBLE_LINE_SEP
;
2214 printfs("\\v'-" DOUBLE_LINE_SEP
"'"
2216 column_divide_reg(start_col
));
2218 prints(left_adjust
);
2220 printfs("\\D'l |\\n[%1]u",
2221 column_divide_reg(end_col
));
2223 prints(right_adjust
);
2225 printfs("\\v'" DOUBLE_LINE_SEP
"'"
2227 column_divide_reg(start_col
));
2229 prints(left_adjust
);
2231 printfs("\\D'l |\\n[%1]u",
2232 column_divide_reg(end_col
));
2234 prints(right_adjust
);
2236 start_col
= end_col
;
2244 void table::compute_vrule_top_adjust(int start_row
, int col
, string
&result
)
2246 if (row_is_all_lines
[start_row
] && start_row
< nrows
- 1) {
2247 if (row_is_all_lines
[start_row
] == 2)
2248 result
= LINE_SEP
">?\\n[.V]u" "+" DOUBLE_LINE_SEP
;
2250 result
= LINE_SEP
">?\\n[.V]u";
2257 for (stuff
*p
= stuff_list
; p
&& p
->row
<= start_row
; p
= p
->next
)
2258 if (p
->row
== start_row
2259 && (p
->is_single_line() || p
->is_double_line()))
2264 table_entry
*e
= entry
[start_row
-1][col
-1];
2265 if (e
&& e
->start_row
== e
->end_row
) {
2266 if (e
->to_double_line_entry() != 0)
2268 else if (e
->to_single_line_entry() != 0)
2273 if (col
< ncolumns
) {
2274 table_entry
*e
= entry
[start_row
-1][col
];
2275 if (e
&& e
->start_row
== e
->end_row
) {
2276 if (e
->to_double_line_entry() != 0)
2278 else if (e
->to_single_line_entry() != 0)
2282 if (row_is_all_lines
[start_row
-1] == 0) {
2283 if (left
> 0 || right
> 0) {
2284 result
+= "-" BODY_DEPTH
"-" BAR_HEIGHT
;
2285 if ((left
== 2 && right
!= 2) || (right
== 2 && left
!= 2))
2286 result
+= "-" HALF_DOUBLE_LINE_SEP
;
2287 else if (left
== 2 && right
== 2)
2288 result
+= "+" HALF_DOUBLE_LINE_SEP
;
2291 else if (row_is_all_lines
[start_row
-1] == 2) {
2292 if ((left
== 2 && right
!= 2) || (right
== 2 && left
!= 2))
2293 result
+= "-" DOUBLE_LINE_SEP
;
2294 else if (left
== 1 || right
== 1)
2295 result
+= "-" HALF_DOUBLE_LINE_SEP
;
2299 void table::compute_vrule_bot_adjust(int end_row
, int col
, string
&result
)
2301 if (row_is_all_lines
[end_row
] && end_row
> 0) {
2306 for (stuff
*p
= stuff_list
; p
&& p
->row
< end_row
+ 1; p
= p
->next
)
2308 if (p
&& p
->row
== end_row
+ 1 && p
->is_double_line()) {
2309 result
= "-" DOUBLE_LINE_SEP
;
2312 if ((p
!= 0 && p
->row
== end_row
+ 1)
2313 || end_row
== nrows
- 1) {
2317 if (row_is_all_lines
[end_row
+1] == 1)
2319 else if (row_is_all_lines
[end_row
+1] == 2)
2320 result
= LINE_SEP
"+" DOUBLE_LINE_SEP
;
2326 table_entry
*e
= entry
[end_row
+1][col
-1];
2327 if (e
&& e
->start_row
== e
->end_row
) {
2328 if (e
->to_double_line_entry() != 0)
2330 else if (e
->to_single_line_entry() != 0)
2335 if (col
< ncolumns
) {
2336 table_entry
*e
= entry
[end_row
+1][col
];
2337 if (e
&& e
->start_row
== e
->end_row
) {
2338 if (e
->to_double_line_entry() != 0)
2340 else if (e
->to_single_line_entry() != 0)
2344 if (row_is_all_lines
[end_row
+1] == 0) {
2345 if (left
> 0 || right
> 0) {
2346 result
= "1v-" BODY_DEPTH
"-" BAR_HEIGHT
;
2347 if ((left
== 2 && right
!= 2) || (right
== 2 && left
!= 2))
2348 result
+= "+" HALF_DOUBLE_LINE_SEP
;
2349 else if (left
== 2 && right
== 2)
2350 result
+= "-" HALF_DOUBLE_LINE_SEP
;
2353 else if (row_is_all_lines
[end_row
+1] == 2) {
2354 if (left
== 2 && right
== 2)
2355 result
+= "-" DOUBLE_LINE_SEP
;
2356 else if (left
!= 2 && right
!= 2 && (left
== 1 || right
== 1))
2357 result
+= "-" HALF_DOUBLE_LINE_SEP
;
2361 void table::add_vertical_rule(int start_row
, int end_row
, int col
, int is_double
)
2363 vrule_list
= new vertical_rule(start_row
, end_row
, col
, is_double
,
2365 compute_vrule_top_adjust(start_row
, col
, vrule_list
->top_adjust
);
2366 compute_vrule_bot_adjust(end_row
, col
, vrule_list
->bot_adjust
);
2369 void table::build_vrule_list()
2372 if (flags
& ALLBOX
) {
2373 for (col
= 1; col
< ncolumns
; col
++) {
2376 while (start_row
< nrows
&& vline_spanned(start_row
, col
))
2378 if (start_row
>= nrows
)
2380 int end_row
= start_row
;
2381 while (end_row
< nrows
&& !vline_spanned(end_row
, col
))
2384 add_vertical_rule(start_row
, end_row
, col
, 0);
2385 start_row
= end_row
+ 1;
2389 if (flags
& (BOX
|ALLBOX
|DOUBLEBOX
)) {
2390 add_vertical_rule(0, nrows
- 1, 0, 0);
2391 add_vertical_rule(0, nrows
- 1, ncolumns
, 0);
2393 for (int end_row
= 0; end_row
< nrows
; end_row
++)
2394 for (col
= 0; col
< ncolumns
+1; col
++)
2395 if (vline
[end_row
][col
] > 0
2396 && !vline_spanned(end_row
, col
)
2397 && (end_row
== nrows
- 1
2398 || vline
[end_row
+1][col
] != vline
[end_row
][col
]
2399 || vline_spanned(end_row
+1, col
))) {
2400 for (int start_row
= end_row
- 1;
2402 && vline
[start_row
][col
] == vline
[end_row
][col
]
2403 && !vline_spanned(start_row
, col
);
2407 add_vertical_rule(start_row
, end_row
, col
, vline
[end_row
][col
] > 1);
2409 for (vertical_rule
*p
= vrule_list
; p
; p
= p
->next
)
2411 for (int r
= p
->start_row
; r
<= p
->end_row
; r
++) {
2412 if (p
->col
> 0 && entry
[r
][p
->col
-1] != 0
2413 && entry
[r
][p
->col
-1]->end_col
== p
->col
-1) {
2414 int is_corner
= r
== p
->start_row
|| r
== p
->end_row
;
2415 entry
[r
][p
->col
-1]->note_double_vrule_on_right(is_corner
);
2417 if (p
->col
< ncolumns
&& entry
[r
][p
->col
] != 0
2418 && entry
[r
][p
->col
]->start_col
== p
->col
) {
2419 int is_corner
= r
== p
->start_row
|| r
== p
->end_row
;
2420 entry
[r
][p
->col
]->note_double_vrule_on_left(is_corner
);
2425 void table::define_bottom_macro()
2429 ".if !\\n[" SUPPRESS_BOTTOM_REG
"] \\{"
2430 "." REPEATED_VPT_MACRO
" 0\n"
2431 ".mk " SAVED_VERTICAL_POS_REG
"\n");
2432 if (flags
& (BOX
|ALLBOX
|DOUBLEBOX
)) {
2433 prints(".if \\n[T.]&\\n[" NEED_BOTTOM_RULE_REG
"] \\{");
2434 print_single_hline(0);
2438 for (vertical_rule
*p
= vrule_list
; p
; p
= p
->next
)
2439 p
->contribute_to_bottom_macro(this);
2440 if (flags
& DOUBLEBOX
)
2441 prints(".if \\n[T.] \\{.vs " DOUBLE_LINE_SEP
">?\\n[.V]u\n"
2442 "\\v'" BODY_DEPTH
"'\\s[\\n[" LINESIZE_REG
"]]"
2443 "\\D'l \\n[TW]u 0'\\s0\n"
2446 ".if \\n[" LAST_PASSED_ROW_REG
"]>=0 "
2447 ".nr " TOP_REG
" \\n[#T]-" DOUBLE_LINE_SEP
"\n"
2449 "\\v'" BODY_DEPTH
"'\\s[\\n[" LINESIZE_REG
"]]"
2450 "\\D'l 0 |\\n[" TOP_REG
"]u-1v'\\s0\n"
2452 "\\v'" BODY_DEPTH
"'\\h'|\\n[TW]u'\\s[\\n[" LINESIZE_REG
"]]"
2453 "\\D'l 0 |\\n[" TOP_REG
"]u-1v'\\s0\n");
2455 prints(".nr " LAST_PASSED_ROW_REG
" \\n[" CURRENT_ROW_REG
"]\n"
2456 ".sp |\\n[" SAVED_VERTICAL_POS_REG
"]u\n"
2457 "." REPEATED_VPT_MACRO
" 1\n"
2464 // is the vertical line before column c in row r horizontally spanned?
2466 int table::vline_spanned(int r
, int c
)
2468 assert(r
>= 0 && r
< nrows
&& c
>= 0 && c
< ncolumns
+ 1);
2469 return (c
!= 0 && c
!= ncolumns
&& entry
[r
][c
] != 0
2470 && entry
[r
][c
]->start_col
!= c
2471 // horizontally spanning lines don't count
2472 && entry
[r
][c
]->to_double_line_entry() == 0
2473 && entry
[r
][c
]->to_single_line_entry() == 0);
2476 int table::row_begins_section(int r
)
2478 assert(r
>= 0 && r
< nrows
);
2479 for (int i
= 0; i
< ncolumns
; i
++)
2480 if (entry
[r
][i
] && entry
[r
][i
]->start_row
!= r
)
2485 int table::row_ends_section(int r
)
2487 assert(r
>= 0 && r
< nrows
);
2488 for (int i
= 0; i
< ncolumns
; i
++)
2489 if (entry
[r
][i
] && entry
[r
][i
]->end_row
!= r
)
2494 void table::do_row(int r
)
2496 if (!(flags
& NOKEEP
) && row_begins_section(r
))
2497 prints("." KEEP_MACRO_NAME
"\n");
2499 for (stuff
*p
= stuff_list
; p
&& p
->row
< r
; p
= p
->next
)
2501 for (stuff
*p1
= p
; p1
&& p1
->row
== r
; p1
= p1
->next
)
2502 if (!p1
->printed
&& (p1
->is_single_line() || p1
->is_double_line())) {
2506 if (!had_line
&& !row_is_all_lines
[r
])
2507 printfs("." REPEATED_MARK_MACRO
" %1\n", row_top_reg(r
));
2509 for (; p
&& p
->row
== r
; p
= p
->next
)
2512 if (!had_line
&& (p
->is_single_line() || p
->is_double_line())) {
2513 printfs("." REPEATED_MARK_MACRO
" %1\n", row_top_reg(r
));
2517 // Change the row *after* printing the stuff list (which might contain .TH).
2518 printfs("\\*[" TRANSPARENT_STRING_NAME
"].nr " CURRENT_ROW_REG
" %1\n",
2520 if (!had_line
&& row_is_all_lines
[r
])
2521 printfs("." REPEATED_MARK_MACRO
" %1\n", row_top_reg(r
));
2522 // we might have had a .TH, for example, since we last tried
2523 if (!(flags
& NOKEEP
) && row_begins_section(r
))
2524 prints("." KEEP_MACRO_NAME
"\n");
2525 printfs(".mk %1\n", row_start_reg(r
));
2526 prints(".mk " BOTTOM_REG
"\n"
2527 "." REPEATED_VPT_MACRO
" 0\n");
2529 int row_is_blank
= 1;
2530 int first_start_row
= r
;
2531 for (c
= 0; c
< ncolumns
; c
++) {
2532 table_entry
*e
= entry
[r
][c
];
2534 if (e
->end_row
== r
) {
2536 if (e
->start_row
< first_start_row
)
2537 first_start_row
= e
->start_row
;
2544 prints(".nr " BOTTOM_REG
" +1v\n");
2545 if (row_is_all_lines
[r
]) {
2546 prints(".vs " LINE_SEP
);
2547 if (row_is_all_lines
[r
] == 2)
2548 prints("+" DOUBLE_LINE_SEP
);
2549 prints(">?\\n[.V]u\n.ls 1\n");
2551 prints("\\v'" BODY_DEPTH
);
2552 if (row_is_all_lines
[r
] == 2)
2553 prints("-" HALF_DOUBLE_LINE_SEP
);
2555 for (c
= 0; c
< ncolumns
; c
++) {
2556 table_entry
*e
= entry
[r
][c
];
2558 if (e
->end_row
== e
->start_row
)
2559 e
->to_simple_entry()->simple_print(1);
2566 prints(".nr " BOTTOM_REG
" \\n[" BOTTOM_REG
"]>?\\n[.d]\n");
2567 printfs(".sp |\\n[%1]u\n", row_start_reg(r
));
2569 for (int i
= row_is_all_lines
[r
] ? r
- 1 : r
;
2570 i
>= first_start_row
;
2572 simple_entry
*first
= 0;
2573 for (c
= 0; c
< ncolumns
; c
++) {
2574 table_entry
*e
= entry
[r
][c
];
2576 if (e
->end_row
== r
&& e
->start_row
== i
) {
2577 simple_entry
*simple
= e
->to_simple_entry();
2591 first
->position_vertically();
2592 first
->set_location();
2594 first
->simple_print(0);
2595 for (c
= first
->end_col
+ 1; c
< ncolumns
; c
++) {
2596 table_entry
*e
= entry
[r
][c
];
2598 if (e
->end_row
== r
&& e
->start_row
== i
) {
2599 simple_entry
*simple
= e
->to_simple_entry();
2601 simple
->simple_print(0);
2607 prints(".nr " BOTTOM_REG
" \\n[" BOTTOM_REG
"]>?\\n[.d]\n");
2608 printfs(".sp |\\n[%1]u\n", row_start_reg(r
));
2611 for (c
= 0; c
< ncolumns
; c
++) {
2612 table_entry
*e
= entry
[r
][c
];
2614 if (e
->end_row
== r
&& e
->to_simple_entry() == 0) {
2615 e
->position_vertically();
2617 prints(".nr " BOTTOM_REG
" \\n[" BOTTOM_REG
"]>?\\n[.d]\n");
2618 printfs(".sp |\\n[%1]u\n", row_start_reg(r
));
2623 prints("." REPEATED_VPT_MACRO
" 1\n"
2624 ".sp |\\n[" BOTTOM_REG
"]u\n"
2625 "\\*[" TRANSPARENT_STRING_NAME
"].nr " NEED_BOTTOM_RULE_REG
" 1\n");
2626 if (r
!= nrows
- 1 && (flags
& ALLBOX
)) {
2627 print_single_hline(r
+ 1);
2628 prints("\\*[" TRANSPARENT_STRING_NAME
"].nr " NEED_BOTTOM_RULE_REG
" 0\n");
2630 if (r
!= nrows
- 1) {
2631 if (p
&& p
->row
== r
+ 1
2632 && (p
->is_single_line() || p
->is_double_line())) {
2634 prints("\\*[" TRANSPARENT_STRING_NAME
"].nr " NEED_BOTTOM_RULE_REG
2637 int printed_one
= 0;
2638 for (vertical_rule
*p
= vrule_list
; p
; p
= p
->next
)
2639 if (p
->end_row
== r
) {
2641 prints("." REPEATED_VPT_MACRO
" 0\n");
2647 prints("." REPEATED_VPT_MACRO
" 1\n");
2648 if (!(flags
& NOKEEP
) && row_ends_section(r
))
2649 prints("." RELEASE_MACRO_NAME
"\n");
2653 void table::do_top()
2655 prints(".fc \002\003\n");
2656 if (!(flags
& NOKEEP
) && (flags
& (BOX
|DOUBLEBOX
|ALLBOX
)))
2657 prints("." TABLE_KEEP_MACRO_NAME
"\n");
2658 if (flags
& DOUBLEBOX
) {
2660 ".vs " LINE_SEP
">?\\n[.V]u\n"
2661 "\\v'" BODY_DEPTH
"'\\s[\\n[" LINESIZE_REG
"]]\\D'l \\n[TW]u 0'\\s0\n"
2663 "." REPEATED_MARK_MACRO
" " TOP_REG
"\n"
2664 ".vs " DOUBLE_LINE_SEP
">?\\n[.V]u\n");
2665 printfs("\\v'" BODY_DEPTH
"'"
2666 "\\s[\\n[" LINESIZE_REG
"]]"
2668 "\\D'l |\\n[%2]u 0'"
2671 column_divide_reg(0),
2672 column_divide_reg(ncolumns
));
2676 else if (flags
& (ALLBOX
|BOX
)) {
2677 print_single_hline(0);
2679 //printfs(".mk %1\n", row_top_reg(0));
2682 void table::do_bottom()
2684 // print stuff after last row
2685 for (stuff
*p
= stuff_list
; p
; p
= p
->next
)
2686 if (p
->row
> nrows
- 1)
2688 if (!(flags
& NOKEEP
))
2689 prints("." RELEASE_MACRO_NAME
"\n");
2690 printfs(".mk %1\n", row_top_reg(nrows
));
2691 prints(".nr " NEED_BOTTOM_RULE_REG
" 1\n"
2694 if (!(flags
& NOKEEP
) && (flags
& (BOX
|DOUBLEBOX
|ALLBOX
)))
2695 prints("." TABLE_RELEASE_MACRO_NAME
"\n");
2696 if (flags
& DOUBLEBOX
)
2697 prints(".sp " DOUBLE_LINE_SEP
"\n");
2698 prints("." RESET_MACRO_NAME
"\n"
2700 ".cp \\n(" COMPATIBLE_REG
"\n");
2703 int table::get_nrows()
2708 const char *last_filename
= 0;
2710 void set_troff_location(const char *fn
, int ln
)
2712 if (!location_force_filename
&& last_filename
!= 0
2713 && strcmp(fn
, last_filename
) == 0)
2714 printfs(".lf %1\n", as_string(ln
));
2716 printfs(".lf %1 %2\n", as_string(ln
), fn
);
2718 location_force_filename
= 0;
2722 void printfs(const char *s
, const string
&arg1
, const string
&arg2
,
2723 const string
&arg3
, const string
&arg4
, const string
&arg5
)
2727 while ((c
= *s
++) != '\0') {