2 /* Copyright (C) 1989-2014 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 3 of the License, or
10 (at your option) any later version.
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
18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
22 #define BAR_HEIGHT ".25m"
23 #define DOUBLE_LINE_SEP "2p"
24 #define HALF_DOUBLE_LINE_SEP "1p"
26 #define BODY_DEPTH ".25m"
28 const int DEFAULT_COLUMN_SEPARATION = 3;
30 #define DELIMITER_CHAR "\\[tbl]"
31 #define SEPARATION_FACTOR_REG PREFIX "sep"
32 #define BOTTOM_REG PREFIX "bot"
33 #define RESET_MACRO_NAME PREFIX "init"
34 #define LINESIZE_REG PREFIX "lps"
35 #define TOP_REG PREFIX "top"
36 #define CURRENT_ROW_REG PREFIX "crow"
37 #define LAST_PASSED_ROW_REG PREFIX "passed"
38 #define TRANSPARENT_STRING_NAME PREFIX "trans"
39 #define QUOTE_STRING_NAME PREFIX "quote"
40 #define SECTION_DIVERSION_NAME PREFIX "section"
41 #define SECTION_DIVERSION_FLAG_REG PREFIX "sflag"
42 #define SAVED_VERTICAL_POS_REG PREFIX "vert"
43 #define NEED_BOTTOM_RULE_REG PREFIX "brule"
44 #define KEEP_MACRO_NAME PREFIX "keep"
45 #define RELEASE_MACRO_NAME PREFIX "release"
46 #define SAVED_FONT_REG PREFIX "fnt"
47 #define SAVED_SIZE_REG PREFIX "sz"
48 #define SAVED_FILL_REG PREFIX "fll"
49 #define SAVED_INDENT_REG PREFIX "ind"
50 #define SAVED_CENTER_REG PREFIX "cent"
51 #define TABLE_DIVERSION_NAME PREFIX "table"
52 #define TABLE_DIVERSION_FLAG_REG PREFIX "tflag"
53 #define TABLE_KEEP_MACRO_NAME PREFIX "tkeep"
54 #define TABLE_RELEASE_MACRO_NAME PREFIX "trelease"
55 #define NEEDED_REG PREFIX "needed"
56 #define REPEATED_MARK_MACRO PREFIX "rmk"
57 #define REPEATED_VPT_MACRO PREFIX "rvpt"
58 #define SUPPRESS_BOTTOM_REG PREFIX "supbot"
59 #define SAVED_DN_REG PREFIX "dn"
60 #define ROW_START_LINE_REG PREFIX "lnst"
61 #define ROW_SAVE_LINE_REG PREFIX "lnsv"
62 #define ROW_MAX_LINE_REG PREFIX "lnmx"
63 #define REPEATED_NM_SET_MACRO PREFIX "rlns"
64 #define REPEATED_NM_SUS_MACRO PREFIX "rlnx"
66 // this must be one character
67 #define COMPATIBLE_REG PREFIX "c"
69 #define EXPAND_REG PREFIX "expand"
71 #define LEADER_REG PREFIX LEADER
73 #define BLOCK_WIDTH_PREFIX PREFIX "tbw"
74 #define BLOCK_DIVERSION_PREFIX PREFIX "tbd"
75 #define BLOCK_HEIGHT_PREFIX PREFIX "tbh"
76 #define SPAN_WIDTH_PREFIX PREFIX "w"
77 #define SPAN_LEFT_NUMERIC_WIDTH_PREFIX PREFIX "lnw"
78 #define SPAN_RIGHT_NUMERIC_WIDTH_PREFIX PREFIX "rnw"
79 #define SPAN_ALPHABETIC_WIDTH_PREFIX PREFIX "aw"
80 #define COLUMN_SEPARATION_PREFIX PREFIX "cs"
81 #define ROW_START_PREFIX PREFIX "rs"
82 #define COLUMN_START_PREFIX PREFIX "cl"
83 #define COLUMN_END_PREFIX PREFIX "ce"
84 #define COLUMN_DIVIDE_PREFIX PREFIX "cd"
85 #define ROW_TOP_PREFIX PREFIX "rt"
87 string block_width_reg(int, int);
88 string block_diversion_name(int, int);
89 string block_height_reg(int, int);
90 string span_width_reg(int, int);
91 string span_left_numeric_width_reg(int, int);
92 string span_right_numeric_width_reg(int, int);
93 string span_alphabetic_width_reg(int, int);
94 string column_separation_reg(int);
95 string row_start_reg(int);
96 string column_start_reg(int);
97 string column_end_reg(int);
98 string column_divide_reg(int);
99 string row_top_reg(int);
101 void set_inline_modifier(const entry_modifier *);
102 void restore_inline_modifier(const entry_modifier *);
103 void set_modifier(const entry_modifier *);
104 int find_decimal_point(const char *, char, const char *);
106 string an_empty_string;
107 int location_force_filename = 0;
109 void printfs(const char *,
110 const string &arg1 = an_empty_string,
111 const string &arg2 = an_empty_string,
112 const string &arg3 = an_empty_string,
113 const string &arg4 = an_empty_string,
114 const string &arg5 = an_empty_string);
116 void prints(const string &);
118 inline void prints(char c)
123 inline void prints(const char *s)
128 void prints(const string &s)
131 fwrite(s.contents(), 1, s.length(), stdout);
134 struct horizontal_span {
135 horizontal_span *next;
138 horizontal_span(int, int, horizontal_span *);
141 class single_line_entry;
142 class double_line_entry;
149 const char *input_filename;
156 const entry_modifier *mod;
159 table_entry(const table *, const entry_modifier *);
160 virtual ~table_entry();
161 virtual int divert(int, const string *, int *, int);
162 virtual void do_width();
163 virtual void do_depth();
164 virtual void print() = 0;
165 virtual void position_vertically() = 0;
166 virtual single_line_entry *to_single_line_entry();
167 virtual double_line_entry *to_double_line_entry();
168 virtual simple_entry *to_simple_entry();
169 virtual int line_type();
170 virtual void note_double_vrule_on_right(int);
171 virtual void note_double_vrule_on_left(int);
174 class simple_entry : public table_entry {
176 simple_entry(const table *, const entry_modifier *);
178 void position_vertically();
179 simple_entry *to_simple_entry();
180 virtual void add_tab();
181 virtual void simple_print(int);
184 class empty_entry : public simple_entry {
186 empty_entry(const table *, const entry_modifier *);
190 class text_entry : public simple_entry {
193 void print_contents();
195 text_entry(const table *, const entry_modifier *, char *);
199 void text_entry::print_contents()
201 set_inline_modifier(mod);
203 restore_inline_modifier(mod);
206 class repeated_char_entry : public text_entry {
208 repeated_char_entry(const table *, const entry_modifier *, char *);
209 void simple_print(int);
212 class simple_text_entry : public text_entry {
214 simple_text_entry(const table *, const entry_modifier *, char *);
218 class left_text_entry : public simple_text_entry {
220 left_text_entry(const table *, const entry_modifier *, char *);
221 void simple_print(int);
225 class right_text_entry : public simple_text_entry {
227 right_text_entry(const table *, const entry_modifier *, char *);
228 void simple_print(int);
232 class center_text_entry : public simple_text_entry {
234 center_text_entry(const table *, const entry_modifier *, char *);
235 void simple_print(int);
239 class numeric_text_entry : public text_entry {
242 numeric_text_entry(const table *, const entry_modifier *, char *, int);
244 void simple_print(int);
247 class alphabetic_text_entry : public text_entry {
249 alphabetic_text_entry(const table *, const entry_modifier *, char *);
251 void simple_print(int);
255 class line_entry : public simple_entry {
257 char double_vrule_on_right;
258 char double_vrule_on_left;
260 line_entry(const table *, const entry_modifier *);
261 void note_double_vrule_on_right(int);
262 void note_double_vrule_on_left(int);
263 void simple_print(int) = 0;
266 class single_line_entry : public line_entry {
268 single_line_entry(const table *, const entry_modifier *);
269 void simple_print(int);
270 single_line_entry *to_single_line_entry();
274 class double_line_entry : public line_entry {
276 double_line_entry(const table *, const entry_modifier *);
277 void simple_print(int);
278 double_line_entry *to_double_line_entry();
282 class short_line_entry : public simple_entry {
284 short_line_entry(const table *, const entry_modifier *);
285 void simple_print(int);
289 class short_double_line_entry : public simple_entry {
291 short_double_line_entry(const table *, const entry_modifier *);
292 void simple_print(int);
296 class block_entry : public table_entry {
299 void do_divert(int, int, const string *, int *, int);
301 block_entry(const table *, const entry_modifier *, char *);
303 int divert(int, const string *, int *, int);
305 void position_vertically();
309 class left_block_entry : public block_entry {
311 left_block_entry(const table *, const entry_modifier *, char *);
315 class right_block_entry : public block_entry {
317 right_block_entry(const table *, const entry_modifier *, char *);
321 class center_block_entry : public block_entry {
323 center_block_entry(const table *, const entry_modifier *, char *);
327 class alphabetic_block_entry : public block_entry {
329 alphabetic_block_entry(const table *, const entry_modifier *, char *);
331 int divert(int, const string *, int *, int);
334 table_entry::table_entry(const table *p, const entry_modifier *m)
335 : next(0), input_lineno(-1), input_filename(0),
336 start_row(-1), end_row(-1), start_col(-1), end_col(-1), parent(p), mod(m)
340 table_entry::~table_entry()
344 int table_entry::divert(int, const string *, int *, int)
349 void table_entry::do_width()
353 single_line_entry *table_entry::to_single_line_entry()
358 double_line_entry *table_entry::to_double_line_entry()
363 simple_entry *table_entry::to_simple_entry()
368 void table_entry::do_depth()
372 void table_entry::set_location()
374 set_troff_location(input_filename, input_lineno);
377 int table_entry::line_type()
382 void table_entry::note_double_vrule_on_right(int)
386 void table_entry::note_double_vrule_on_left(int)
390 simple_entry::simple_entry(const table *p, const entry_modifier *m)
395 void simple_entry::add_tab()
400 void simple_entry::simple_print(int)
405 void simple_entry::position_vertically()
407 if (start_row != end_row)
408 switch (mod->vertical_alignment) {
409 case entry_modifier::TOP:
410 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
412 case entry_modifier::CENTER:
413 // Peform the motion in two stages so that the center is rounded
414 // vertically upwards even if net vertical motion is upwards.
415 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
416 printfs(".sp \\n[" BOTTOM_REG "]u-\\n[%1]u-1v/2u\n",
417 row_start_reg(start_row));
419 case entry_modifier::BOTTOM:
420 printfs(".sp |\\n[%1]u+\\n[" BOTTOM_REG "]u-\\n[%1]u-1v\n",
421 row_start_reg(start_row));
428 void simple_entry::print()
439 simple_entry *simple_entry::to_simple_entry()
444 empty_entry::empty_entry(const table *p, const entry_modifier *m)
449 int empty_entry::line_type()
454 text_entry::text_entry(const table *p, const entry_modifier *m, char *s)
455 : simple_entry(p, m), contents(s)
459 text_entry::~text_entry()
464 repeated_char_entry::repeated_char_entry(const table *p,
465 const entry_modifier *m, char *s)
466 : text_entry(p, m, s)
470 void repeated_char_entry::simple_print(int)
472 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
473 set_inline_modifier(mod);
474 printfs("\\l" DELIMITER_CHAR "\\n[%1]u\\&",
475 span_width_reg(start_col, end_col));
477 prints(DELIMITER_CHAR);
478 restore_inline_modifier(mod);
481 simple_text_entry::simple_text_entry(const table *p,
482 const entry_modifier *m, char *s)
483 : text_entry(p, m, s)
487 void simple_text_entry::do_width()
490 printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR,
491 span_width_reg(start_col, end_col));
493 prints(DELIMITER_CHAR "\n");
496 left_text_entry::left_text_entry(const table *p,
497 const entry_modifier *m, char *s)
498 : simple_text_entry(p, m, s)
502 void left_text_entry::simple_print(int)
504 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
508 // The only point of this is to make `\a' ``work'' as in Unix tbl. Grrr.
510 void left_text_entry::add_tab()
512 printfs(" \\n[%1]u", column_end_reg(end_col));
515 right_text_entry::right_text_entry(const table *p,
516 const entry_modifier *m, char *s)
517 : simple_text_entry(p, m, s)
521 void right_text_entry::simple_print(int)
523 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
529 void right_text_entry::add_tab()
531 printfs(" \\n[%1]u", column_end_reg(end_col));
534 center_text_entry::center_text_entry(const table *p,
535 const entry_modifier *m, char *s)
536 : simple_text_entry(p, m, s)
540 void center_text_entry::simple_print(int)
542 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
548 void center_text_entry::add_tab()
550 printfs(" \\n[%1]u", column_end_reg(end_col));
553 numeric_text_entry::numeric_text_entry(const table *p,
554 const entry_modifier *m,
556 : text_entry(p, m, s), dot_pos(pos)
560 void numeric_text_entry::do_width()
564 printfs(".nr %1 0\\w" DELIMITER_CHAR,
565 block_width_reg(start_row, start_col));
566 set_inline_modifier(mod);
567 for (int i = 0; i < dot_pos; i++)
569 restore_inline_modifier(mod);
570 prints(DELIMITER_CHAR "\n");
571 printfs(".nr %1 \\n[%1]>?\\n[%2]\n",
572 span_left_numeric_width_reg(start_col, end_col),
573 block_width_reg(start_row, start_col));
576 printfs(".nr %1 0\n", block_width_reg(start_row, start_col));
577 if (contents[dot_pos] != '\0') {
579 printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR,
580 span_right_numeric_width_reg(start_col, end_col));
581 set_inline_modifier(mod);
582 prints(contents + dot_pos);
583 restore_inline_modifier(mod);
584 prints(DELIMITER_CHAR "\n");
588 void numeric_text_entry::simple_print(int)
590 printfs("\\h'|(\\n[%1]u-\\n[%2]u-\\n[%3]u/2u+\\n[%2]u+\\n[%4]u-\\n[%5]u)'",
591 span_width_reg(start_col, end_col),
592 span_left_numeric_width_reg(start_col, end_col),
593 span_right_numeric_width_reg(start_col, end_col),
594 column_start_reg(start_col),
595 block_width_reg(start_row, start_col));
599 alphabetic_text_entry::alphabetic_text_entry(const table *p,
600 const entry_modifier *m,
602 : text_entry(p, m, s)
606 void alphabetic_text_entry::do_width()
609 printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR,
610 span_alphabetic_width_reg(start_col, end_col));
612 prints(DELIMITER_CHAR "\n");
615 void alphabetic_text_entry::simple_print(int)
617 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
618 printfs("\\h'\\n[%1]u-\\n[%2]u/2u'",
619 span_width_reg(start_col, end_col),
620 span_alphabetic_width_reg(start_col, end_col));
624 // The only point of this is to make `\a' ``work'' as in Unix tbl. Grrr.
626 void alphabetic_text_entry::add_tab()
628 printfs(" \\n[%1]u", column_end_reg(end_col));
631 block_entry::block_entry(const table *p, const entry_modifier *m, char *s)
632 : table_entry(p, m), contents(s)
636 block_entry::~block_entry()
641 void block_entry::position_vertically()
643 if (start_row != end_row)
644 switch(mod->vertical_alignment) {
645 case entry_modifier::TOP:
646 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
648 case entry_modifier::CENTER:
649 // Peform the motion in two stages so that the center is rounded
650 // vertically upwards even if net vertical motion is upwards.
651 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
652 printfs(".sp \\n[" BOTTOM_REG "]u-\\n[%1]u-\\n[%2]u/2u\n",
653 row_start_reg(start_row),
654 block_height_reg(start_row, start_col));
656 case entry_modifier::BOTTOM:
657 printfs(".sp |\\n[%1]u+\\n[" BOTTOM_REG "]u-\\n[%1]u-\\n[%2]u\n",
658 row_start_reg(start_row),
659 block_height_reg(start_row, start_col));
665 prints(".sp -.5v\n");
668 int block_entry::divert(int ncols, const string *mw, int *sep, int do_expand)
670 do_divert(0, ncols, mw, sep, do_expand);
674 void block_entry::do_divert(int alphabetic, int ncols, const string *mw,
675 int *sep, int do_expand)
678 for (i = start_col; i <= end_col; i++)
679 if (parent->expand[i])
689 printfs(".di %1\n", block_diversion_name(start_row, start_col));
690 prints(".if \\n[" SAVED_FILL_REG "] .fi\n"
693 for (i = start_col; i <= end_col; i++)
694 if (mw[i].empty() && !parent->expand[i])
697 // Every column spanned by this entry has a minimum width.
698 for (int j = start_col; j <= end_col; j++) {
701 printfs("+%1n", as_string(sep[j - 1]));
704 if (parent->expand[j])
705 prints("\\n[" EXPAND_REG "]u");
707 printfs("(n;%1)", mw[j]);
709 printfs(">?\\n[%1]u", span_width_reg(start_col, end_col));
712 // Assign each column with a block entry 1/(n+1) of the line
713 // width, where n is the column count.
714 printfs("(u;\\n[%1]>?(\\n[.l]*%2/%3))",
715 span_width_reg(start_col, end_col),
716 as_string(end_col - start_col + 1),
717 as_string(ncols + 1));
721 prints(".cp \\n(" COMPATIBLE_REG "\n");
725 prints(".br\n.di\n.cp 0\n");
726 if (!mod->zero_width) {
728 printfs(".nr %1 \\n[%1]>?(\\n[dl]+2n)\n",
729 span_width_reg(start_col, end_col));
730 printfs(".nr %1 \\n[%1]>?\\n[dl]\n",
731 span_alphabetic_width_reg(start_col, end_col));
734 printfs(".nr %1 \\n[%1]>?\\n[dl]\n",
735 span_width_reg(start_col, end_col));
737 printfs(".nr %1 \\n[dn]\n", block_height_reg(start_row, start_col));
738 printfs(".nr %1 \\n[dl]\n", block_width_reg(start_row, start_col));
739 prints("." RESET_MACRO_NAME "\n"
740 ".in \\n[" SAVED_INDENT_REG "]u\n"
742 // the block might have contained .lf commands
743 location_force_filename = 1;
746 void block_entry::do_depth()
748 printfs(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?(\\n[%1]+\\n[%2])\n",
749 row_start_reg(start_row),
750 block_height_reg(start_row, start_col));
753 left_block_entry::left_block_entry(const table *p,
754 const entry_modifier *m, char *s)
755 : block_entry(p, m, s)
759 void left_block_entry::print()
761 printfs(".in +\\n[%1]u\n", column_start_reg(start_col));
762 printfs(".%1\n", block_diversion_name(start_row, start_col));
766 right_block_entry::right_block_entry(const table *p,
767 const entry_modifier *m, char *s)
768 : block_entry(p, m, s)
772 void right_block_entry::print()
774 printfs(".in +\\n[%1]u+\\n[%2]u-\\n[%3]u\n",
775 column_start_reg(start_col),
776 span_width_reg(start_col, end_col),
777 block_width_reg(start_row, start_col));
778 printfs(".%1\n", block_diversion_name(start_row, start_col));
782 center_block_entry::center_block_entry(const table *p,
783 const entry_modifier *m, char *s)
784 : block_entry(p, m, s)
788 void center_block_entry::print()
790 printfs(".in +\\n[%1]u+(\\n[%2]u-\\n[%3]u/2u)\n",
791 column_start_reg(start_col),
792 span_width_reg(start_col, end_col),
793 block_width_reg(start_row, start_col));
794 printfs(".%1\n", block_diversion_name(start_row, start_col));
798 alphabetic_block_entry::alphabetic_block_entry(const table *p,
799 const entry_modifier *m,
801 : block_entry(p, m, s)
805 int alphabetic_block_entry::divert(int ncols, const string *mw, int *sep,
808 do_divert(1, ncols, mw, sep, do_expand);
812 void alphabetic_block_entry::print()
814 printfs(".in +\\n[%1]u+(\\n[%2]u-\\n[%3]u/2u)\n",
815 column_start_reg(start_col),
816 span_width_reg(start_col, end_col),
817 span_alphabetic_width_reg(start_col, end_col));
818 printfs(".%1\n", block_diversion_name(start_row, start_col));
822 line_entry::line_entry(const table *p, const entry_modifier *m)
823 : simple_entry(p, m), double_vrule_on_right(0), double_vrule_on_left(0)
827 void line_entry::note_double_vrule_on_right(int is_corner)
829 double_vrule_on_right = is_corner ? 1 : 2;
832 void line_entry::note_double_vrule_on_left(int is_corner)
834 double_vrule_on_left = is_corner ? 1 : 2;
837 single_line_entry::single_line_entry(const table *p, const entry_modifier *m)
842 int single_line_entry::line_type()
847 void single_line_entry::simple_print(int dont_move)
849 printfs("\\h'|\\n[%1]u",
850 column_divide_reg(start_col));
851 if (double_vrule_on_left) {
852 prints(double_vrule_on_left == 1 ? "-" : "+");
853 prints(HALF_DOUBLE_LINE_SEP);
857 prints("\\v'-" BAR_HEIGHT "'");
858 printfs("\\s[\\n[" LINESIZE_REG "]]" "\\D'l |\\n[%1]u",
859 column_divide_reg(end_col+1));
860 if (double_vrule_on_right) {
861 prints(double_vrule_on_left == 1 ? "+" : "-");
862 prints(HALF_DOUBLE_LINE_SEP);
866 prints("\\v'" BAR_HEIGHT "'");
869 single_line_entry *single_line_entry::to_single_line_entry()
874 double_line_entry::double_line_entry(const table *p, const entry_modifier *m)
879 int double_line_entry::line_type()
884 void double_line_entry::simple_print(int dont_move)
887 prints("\\v'-" BAR_HEIGHT "'");
888 printfs("\\h'|\\n[%1]u",
889 column_divide_reg(start_col));
890 if (double_vrule_on_left) {
891 prints(double_vrule_on_left == 1 ? "-" : "+");
892 prints(HALF_DOUBLE_LINE_SEP);
895 printfs("\\v'-" HALF_DOUBLE_LINE_SEP "'"
896 "\\s[\\n[" LINESIZE_REG "]]"
898 column_divide_reg(end_col+1));
899 if (double_vrule_on_right)
900 prints("-" HALF_DOUBLE_LINE_SEP);
902 printfs("\\v'" DOUBLE_LINE_SEP "'"
904 column_divide_reg(start_col));
905 if (double_vrule_on_right) {
906 prints(double_vrule_on_left == 1 ? "+" : "-");
907 prints(HALF_DOUBLE_LINE_SEP);
911 "\\v'-" HALF_DOUBLE_LINE_SEP "'");
913 prints("\\v'" BAR_HEIGHT "'");
916 double_line_entry *double_line_entry::to_double_line_entry()
921 short_line_entry::short_line_entry(const table *p, const entry_modifier *m)
926 int short_line_entry::line_type()
931 void short_line_entry::simple_print(int dont_move)
936 prints("\\v'-" BAR_HEIGHT "'");
937 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
938 printfs("\\s[\\n[" LINESIZE_REG "]]"
941 span_width_reg(start_col, end_col));
943 prints("\\v'" BAR_HEIGHT "'");
948 short_double_line_entry::short_double_line_entry(const table *p,
949 const entry_modifier *m)
954 int short_double_line_entry::line_type()
959 void short_double_line_entry::simple_print(int dont_move)
964 prints("\\v'-" BAR_HEIGHT "'");
965 printfs("\\h'|\\n[%2]u'"
966 "\\v'-" HALF_DOUBLE_LINE_SEP "'"
967 "\\s[\\n[" LINESIZE_REG "]]"
969 "\\v'" DOUBLE_LINE_SEP "'"
972 "\\v'-" HALF_DOUBLE_LINE_SEP "'",
973 span_width_reg(start_col, end_col),
974 column_start_reg(start_col));
976 prints("\\v'" BAR_HEIGHT "'");
981 void set_modifier(const entry_modifier *m)
983 if (!m->font.empty())
984 printfs(".ft %1\n", m->font);
985 if (m->point_size.val != 0) {
987 if (m->point_size.inc > 0)
989 else if (m->point_size.inc < 0)
991 printfs("%1\n", as_string(m->point_size.val));
993 if (m->vertical_spacing.val != 0) {
995 if (m->vertical_spacing.inc > 0)
997 else if (m->vertical_spacing.inc < 0)
999 printfs("%1\n", as_string(m->vertical_spacing.val));
1001 if (!m->macro.empty())
1002 printfs(".%1\n", m->macro);
1005 void set_inline_modifier(const entry_modifier *m)
1007 if (!m->font.empty())
1008 printfs("\\f[%1]", m->font);
1009 if (m->point_size.val != 0) {
1011 if (m->point_size.inc > 0)
1013 else if (m->point_size.inc < 0)
1015 printfs("%1]", as_string(m->point_size.val));
1018 prints("\\v'-.5v'");
1021 void restore_inline_modifier(const entry_modifier *m)
1023 if (!m->font.empty())
1024 prints("\\f[\\n[" SAVED_FONT_REG "]]");
1025 if (m->point_size.val != 0)
1026 prints("\\s[\\n[" SAVED_SIZE_REG "]]");
1033 int row; // occurs before row `row'
1034 char printed; // has it been printed?
1037 virtual void print(table *) = 0;
1039 virtual int is_single_line() { return 0; };
1040 virtual int is_double_line() { return 0; };
1043 stuff::stuff(int r) : next(0), row(r), printed(0)
1051 struct text_stuff : public stuff {
1053 const char *filename;
1056 text_stuff(const string &, int, const char *, int);
1058 void print(table *);
1061 text_stuff::text_stuff(const string &s, int r, const char *fn, int ln)
1062 : stuff(r), contents(s), filename(fn), lineno(ln)
1066 text_stuff::~text_stuff()
1070 void text_stuff::print(table *)
1073 prints(".cp \\n(" COMPATIBLE_REG "\n");
1074 set_troff_location(filename, lineno);
1077 location_force_filename = 1; // it might have been a .lf command
1080 struct single_hline_stuff : public stuff {
1081 single_hline_stuff(int);
1082 void print(table *);
1083 int is_single_line();
1086 single_hline_stuff::single_hline_stuff(int r) : stuff(r)
1090 void single_hline_stuff::print(table *tbl)
1093 tbl->print_single_hline(row);
1096 int single_hline_stuff::is_single_line()
1101 struct double_hline_stuff : stuff {
1102 double_hline_stuff(int);
1103 void print(table *);
1104 int is_double_line();
1107 double_hline_stuff::double_hline_stuff(int r) : stuff(r)
1111 void double_hline_stuff::print(table *tbl)
1114 tbl->print_double_hline(row);
1117 int double_hline_stuff::is_double_line()
1122 struct vertical_rule {
1123 vertical_rule *next;
1131 vertical_rule(int, int, int, int, vertical_rule *);
1133 void contribute_to_bottom_macro(table *);
1137 vertical_rule::vertical_rule(int sr, int er, int c, int dbl,
1139 : next(p), start_row(sr), end_row(er), col(c), is_double(dbl)
1143 vertical_rule::~vertical_rule()
1147 void vertical_rule::contribute_to_bottom_macro(table *tbl)
1149 printfs(".if \\n[" CURRENT_ROW_REG "]>=%1",
1150 as_string(start_row));
1151 if (end_row != tbl->get_nrows() - 1)
1152 printfs("&(\\n[" CURRENT_ROW_REG "]<%1)",
1153 as_string(end_row));
1155 printfs(".if %1<=\\n[" LAST_PASSED_ROW_REG "] .nr %2 \\n[#T]\n",
1156 as_string(start_row),
1157 row_top_reg(start_row));
1158 const char *offset_table[3];
1160 offset_table[0] = "-" HALF_DOUBLE_LINE_SEP;
1161 offset_table[1] = "+" HALF_DOUBLE_LINE_SEP;
1162 offset_table[2] = 0;
1165 offset_table[0] = "";
1166 offset_table[1] = 0;
1168 for (const char **offsetp = offset_table; *offsetp; offsetp++) {
1171 if (!bot_adjust.empty())
1172 printfs("+%1", bot_adjust);
1174 printfs("\\h'\\n[%1]u%3'\\s[\\n[" LINESIZE_REG "]]\\D'l 0 |\\n[%2]u-1v",
1175 column_divide_reg(col),
1176 row_top_reg(start_row),
1178 if (!bot_adjust.empty())
1179 printfs("-(%1)", bot_adjust);
1180 // don't perform the top adjustment if the top is actually #T
1181 if (!top_adjust.empty())
1182 printfs("+((%1)*(%2>\\n[" LAST_PASSED_ROW_REG "]))",
1184 as_string(start_row));
1190 void vertical_rule::print()
1192 printfs("\\*[" TRANSPARENT_STRING_NAME "]"
1193 ".if %1<=\\*[" QUOTE_STRING_NAME "]\\n[" LAST_PASSED_ROW_REG "] "
1194 ".nr %2 \\*[" QUOTE_STRING_NAME "]\\n[#T]\n",
1195 as_string(start_row),
1196 row_top_reg(start_row));
1197 const char *offset_table[3];
1199 offset_table[0] = "-" HALF_DOUBLE_LINE_SEP;
1200 offset_table[1] = "+" HALF_DOUBLE_LINE_SEP;
1201 offset_table[2] = 0;
1204 offset_table[0] = "";
1205 offset_table[1] = 0;
1207 for (const char **offsetp = offset_table; *offsetp; offsetp++) {
1208 prints("\\*[" TRANSPARENT_STRING_NAME "].sp -1\n"
1209 "\\*[" TRANSPARENT_STRING_NAME "]\\v'" BODY_DEPTH);
1210 if (!bot_adjust.empty())
1211 printfs("+%1", bot_adjust);
1213 printfs("\\h'\\n[%1]u%3'"
1214 "\\s[\\n[" LINESIZE_REG "]]"
1215 "\\D'l 0 |\\*[" QUOTE_STRING_NAME "]\\n[%2]u-1v",
1216 column_divide_reg(col),
1217 row_top_reg(start_row),
1219 if (!bot_adjust.empty())
1220 printfs("-(%1)", bot_adjust);
1221 // don't perform the top adjustment if the top is actually #T
1222 if (!top_adjust.empty())
1223 printfs("+((%1)*(%2>\\*[" QUOTE_STRING_NAME "]\\n["
1224 LAST_PASSED_ROW_REG "]))",
1226 as_string(start_row));
1232 table::table(int nc, unsigned f, int ls, char dpc)
1233 : nrows(0), ncolumns(nc), linesize(ls), decimal_point_char(dpc),
1234 vrule_list(0), stuff_list(0), span_list(0),
1235 entry_list(0), entry_list_tailp(&entry_list), entry(0),
1236 vline(0), row_is_all_lines(0), left_separation(0), right_separation(0),
1237 total_separation(0), allocated_rows(0), flags(f)
1239 minimum_width = new string[ncolumns];
1240 column_separation = ncolumns > 1 ? new int[ncolumns - 1] : 0;
1241 equal = new char[ncolumns];
1242 expand = new char[ncolumns];
1244 for (i = 0; i < ncolumns; i++) {
1248 for (i = 0; i < ncolumns - 1; i++)
1249 column_separation[i] = DEFAULT_COLUMN_SEPARATION;
1250 delim[0] = delim[1] = '\0';
1255 for (int i = 0; i < nrows; i++) {
1261 while (entry_list) {
1262 table_entry *tem = entry_list;
1263 entry_list = entry_list->next;
1266 ad_delete(ncolumns) minimum_width;
1267 a_delete column_separation;
1270 while (stuff_list) {
1271 stuff *tem = stuff_list;
1272 stuff_list = stuff_list->next;
1275 while (vrule_list) {
1276 vertical_rule *tem = vrule_list;
1277 vrule_list = vrule_list->next;
1280 a_delete row_is_all_lines;
1282 horizontal_span *tem = span_list;
1283 span_list = span_list->next;
1288 void table::set_delim(char c1, char c2)
1294 void table::set_minimum_width(int c, const string &w)
1296 assert(c >= 0 && c < ncolumns);
1297 minimum_width[c] = w;
1300 void table::set_column_separation(int c, int n)
1302 assert(c >= 0 && c < ncolumns - 1);
1303 column_separation[c] = n;
1306 void table::set_equal_column(int c)
1308 assert(c >= 0 && c < ncolumns);
1312 void table::set_expand_column(int c)
1314 assert(c >= 0 && c < ncolumns);
1318 void table::add_stuff(stuff *p)
1321 for (pp = &stuff_list; *pp; pp = &(*pp)->next)
1326 void table::add_text_line(int r, const string &s, const char *filename,
1329 add_stuff(new text_stuff(s, r, filename, lineno));
1332 void table::add_single_hline(int r)
1334 add_stuff(new single_hline_stuff(r));
1337 void table::add_double_hline(int r)
1339 add_stuff(new double_hline_stuff(r));
1342 void table::allocate(int r)
1345 typedef table_entry **PPtable_entry; // work around g++ 1.36.1 bug
1346 if (r >= allocated_rows) {
1347 if (allocated_rows == 0) {
1348 allocated_rows = 16;
1349 if (allocated_rows <= r)
1350 allocated_rows = r + 1;
1351 entry = new PPtable_entry[allocated_rows];
1352 vline = new char*[allocated_rows];
1355 table_entry ***old_entry = entry;
1356 int old_allocated_rows = allocated_rows;
1357 allocated_rows *= 2;
1358 if (allocated_rows <= r)
1359 allocated_rows = r + 1;
1360 entry = new PPtable_entry[allocated_rows];
1361 memcpy(entry, old_entry, sizeof(table_entry**)*old_allocated_rows);
1363 char **old_vline = vline;
1364 vline = new char*[allocated_rows];
1365 memcpy(vline, old_vline, sizeof(char*)*old_allocated_rows);
1369 assert(allocated_rows > r);
1370 while (nrows <= r) {
1371 entry[nrows] = new table_entry*[ncolumns];
1373 for (i = 0; i < ncolumns; i++)
1374 entry[nrows][i] = 0;
1375 vline[nrows] = new char[ncolumns+1];
1376 for (i = 0; i < ncolumns+1; i++)
1377 vline[nrows][i] = 0;
1383 void table::do_hspan(int r, int c)
1385 assert(r >= 0 && c >= 0 && r < nrows && c < ncolumns);
1387 error("first column cannot be horizontally spanned");
1390 table_entry *e = entry[r][c];
1392 assert(e->start_row <= r && r <= e->end_row
1393 && e->start_col <= c && c <= e->end_col
1394 && e->end_row - e->start_row > 0
1395 && e->end_col - e->start_col > 0);
1399 // e can be 0 if we had an empty entry or an error
1402 if (e->start_row != r) {
1406 error("impossible horizontal span at row %1, column %2", r + 1, c + 1);
1414 void table::do_vspan(int r, int c)
1416 assert(r >= 0 && c >= 0 && r < nrows && c < ncolumns);
1418 error("first row cannot be vertically spanned");
1421 table_entry *e = entry[r][c];
1423 assert(e->start_row <= r && r <= e->end_row
1424 && e->start_col <= c && c <= e->end_col
1425 && e->end_row - e->start_row > 0
1426 && e->end_col - e->start_col > 0);
1430 // e can be 0 if we had an empty entry or an error
1433 if (e->start_col != c) {
1436 error("impossible vertical span at row %1, column %2", r + 1, c + 1);
1439 for (int i = c; i <= e->end_col; i++) {
1440 assert(entry[r][i] == 0);
1447 int find_decimal_point(const char *s, char decimal_point_char,
1450 if (s == 0 || *s == '\0')
1453 int in_delim = 0; // is p within eqn delimiters?
1454 // tbl recognises \& even within eqn delimiters; I don't
1455 for (p = s; *p; p++)
1460 else if (*p == delim[0])
1462 else if (p[0] == '\\' && p[1] == '&')
1464 int possible_pos = -1;
1466 for (p = s; *p; p++)
1471 else if (*p == delim[0])
1473 else if (p[0] == decimal_point_char && csdigit(p[1]))
1474 possible_pos = p - s;
1475 if (possible_pos >= 0)
1476 return possible_pos;
1478 for (p = s; *p; p++)
1483 else if (*p == delim[0])
1485 else if (csdigit(*p))
1486 possible_pos = p + 1 - s;
1487 return possible_pos;
1490 void table::add_entry(int r, int c, const string &str, const entry_format *f,
1491 const char *fn, int ln)
1496 e = new short_line_entry(this, f);
1498 else if (str == "\\=") {
1499 e = new short_double_line_entry(this, f);
1501 else if (str == "_") {
1502 single_line_entry *lefte;
1503 if (c > 0 && entry[r][c-1] != 0 &&
1504 (lefte = entry[r][c-1]->to_single_line_entry()) != 0
1505 && lefte->start_row == r
1506 && lefte->mod->stagger == f->stagger) {
1508 entry[r][c] = lefte;
1511 e = new single_line_entry(this, f);
1513 else if (str == "=") {
1514 double_line_entry *lefte;
1515 if (c > 0 && entry[r][c-1] != 0 &&
1516 (lefte = entry[r][c-1]->to_double_line_entry()) != 0
1517 && lefte->start_row == r
1518 && lefte->mod->stagger == f->stagger) {
1520 entry[r][c] = lefte;
1523 e = new double_line_entry(this, f);
1525 else if (str == "\\^") {
1528 else if (str.length() > 2 && str[0] == '\\' && str[1] == 'R') {
1529 if (str.search('\n') >= 0)
1530 error_with_file_and_line(fn, ln, "bad repeated character");
1532 char *s = str.substring(2, str.length() - 2).extract();
1533 e = new repeated_char_entry(this, f, s);
1537 int is_block = str.search('\n') >= 0;
1541 assert(str.empty());
1548 e = new left_block_entry(this, f, s);
1550 e = new left_text_entry(this, f, s);
1553 e = new empty_entry(this, f);
1559 e = new center_block_entry(this, f, s);
1561 e = new center_text_entry(this, f, s);
1564 e = new empty_entry(this, f);
1570 e = new right_block_entry(this, f, s);
1572 e = new right_text_entry(this, f, s);
1575 e = new empty_entry(this, f);
1577 case FORMAT_NUMERIC:
1581 error_with_file_and_line(fn, ln, "can't have numeric text block");
1582 e = new left_block_entry(this, f, s);
1585 int pos = find_decimal_point(s, decimal_point_char, delim);
1587 e = new center_text_entry(this, f, s);
1589 e = new numeric_text_entry(this, f, s, pos);
1593 e = new empty_entry(this, f);
1595 case FORMAT_ALPHABETIC:
1599 e = new alphabetic_block_entry(this, f, s);
1601 e = new alphabetic_text_entry(this, f, s);
1604 e = new empty_entry(this, f);
1610 if (str.length() != 0)
1611 error_with_file_and_line(fn, ln,
1612 "non-empty data entry for `_' format ignored");
1613 e = new single_line_entry(this, f);
1615 case FORMAT_DOUBLE_HLINE:
1616 if (str.length() != 0)
1617 error_with_file_and_line(fn, ln,
1618 "non-empty data entry for `=' format ignored");
1619 e = new double_line_entry(this, f);
1626 table_entry *preve = entry[r][c];
1630 error_with_file_and_line(fn, ln, "row %1, column %2 already spanned",
1635 e->input_lineno = ln;
1636 e->input_filename = fn;
1637 e->start_row = e->end_row = r;
1638 e->start_col = e->end_col = c;
1639 *entry_list_tailp = e;
1640 entry_list_tailp = &e->next;
1646 // add vertical lines for row r
1648 void table::add_vlines(int r, const char *v)
1651 for (int i = 0; i < ncolumns+1; i++)
1657 table_entry *p = entry_list;
1660 for (i = p->start_row; i <= p->end_row; i++)
1661 for (j = p->start_col; j <= p->end_col; j++)
1662 assert(entry[i][j] == p);
1669 location_force_filename = 1;
1672 determine_row_type();
1674 if (!(flags & CENTER))
1675 prints(".if \\n[" SAVED_CENTER_REG "] \\{");
1676 prints(".in +(u;\\n[.l]-\\n[.i]-\\n[TW]/2>?-\\n[.i])\n"
1677 ".nr " SAVED_INDENT_REG " \\n[.i]\n");
1678 if (!(flags & CENTER))
1681 define_bottom_macro();
1683 for (int i = 0; i < nrows; i++)
1688 void table::determine_row_type()
1690 row_is_all_lines = new char[nrows];
1691 for (int i = 0; i < nrows; i++) {
1694 int had_non_line = 0;
1695 for (int c = 0; c < ncolumns; c++) {
1696 table_entry *e = entry[i][c];
1698 if (e->start_row == e->end_row) {
1699 int t = e->line_type();
1723 row_is_all_lines[i] = 0;
1724 else if (had_double)
1725 row_is_all_lines[i] = 2;
1726 else if (had_single)
1727 row_is_all_lines[i] = 1;
1729 row_is_all_lines[i] = 0;
1733 int table::count_expand_columns()
1736 for (int i = 0; i < ncolumns; i++)
1742 void table::init_output()
1744 prints(".nr " COMPATIBLE_REG " \\n(.C\n"
1747 printfs(".nr " LINESIZE_REG " %1\n", as_string(linesize));
1749 prints(".nr " LINESIZE_REG " \\n[.s]\n");
1750 if (!(flags & CENTER))
1751 prints(".nr " SAVED_CENTER_REG " \\n[.ce]\n");
1752 if (compatible_flag)
1753 prints(".ds " LEADER_REG " \\a\n");
1754 prints(".de " RESET_MACRO_NAME "\n"
1766 ".nr " SAVED_INDENT_REG " \\n[.i]\n"
1767 ".nr " SAVED_FONT_REG " \\n[.f]\n"
1768 ".nr " SAVED_SIZE_REG " \\n[.s]\n"
1769 ".nr " SAVED_FILL_REG " \\n[.u]\n"
1771 ".nr " CURRENT_ROW_REG " 0-1\n"
1772 ".nr " LAST_PASSED_ROW_REG " 0-1\n"
1773 ".nr " SECTION_DIVERSION_FLAG_REG " 0\n"
1774 ".ds " TRANSPARENT_STRING_NAME "\n"
1775 ".ds " QUOTE_STRING_NAME "\n"
1776 ".nr " NEED_BOTTOM_RULE_REG " 1\n"
1777 ".nr " SUPPRESS_BOTTOM_REG " 0\n"
1779 ".de " REPEATED_MARK_MACRO "\n"
1781 ".if !'\\n(.z'' \\!." REPEATED_MARK_MACRO " \"\\$1\"\n"
1783 ".de " REPEATED_VPT_MACRO "\n"
1785 ".if !'\\n(.z'' \\!." REPEATED_VPT_MACRO " \"\\$1\"\n"
1787 ".de " REPEATED_NM_SET_MACRO "\n"
1788 ".ie !'\\n(.z'' \\{.nm\n"
1789 "\\!." REPEATED_NM_SET_MACRO " \"\\$1\"\n"
1791 ".el .if \\n[ln] \\{\\\n"
1792 ".if '\\$1'd' .nr " ROW_START_LINE_REG " \\n[ln]\n"
1793 ".if '\\$1's' .nm \\n[" ROW_START_LINE_REG "]\n"
1794 ".if '\\$1'm' .nr " ROW_MAX_LINE_REG " \\n[ln]>?\\n[" ROW_MAX_LINE_REG "]\n"
1797 ".de " REPEATED_NM_SUS_MACRO "\n"
1798 ".ie !'\\n(.z'' \\{.nm\n"
1799 "\\!." REPEATED_NM_SUS_MACRO " \"\\$1\"\n"
1801 ".el .if \\n[ln] \\{\\\n"
1802 ".ie '\\$1's' \\{\\\n"
1803 ".nr " ROW_SAVE_LINE_REG " \\n(ln<?\\n[" ROW_MAX_LINE_REG "]\n"
1804 ".nm +0 \\n[ln]+42\n"
1807 ".nr ln \\n[" ROW_SAVE_LINE_REG "]\n"
1812 if (!(flags & NOKEEP))
1813 prints(".de " KEEP_MACRO_NAME "\n"
1814 ".if '\\n[.z]'' \\{.ds " QUOTE_STRING_NAME " \\\\\n"
1815 ".ds " TRANSPARENT_STRING_NAME " \\!\n"
1816 ".di " SECTION_DIVERSION_NAME "\n"
1817 ".nr " SECTION_DIVERSION_FLAG_REG " 1\n"
1821 // protect # in macro name against eqn
1827 ".de " RELEASE_MACRO_NAME "\n"
1828 ".if \\n[" SECTION_DIVERSION_FLAG_REG "] \\{"
1830 ".in \\n[" SAVED_INDENT_REG "]u\n"
1831 ".nr " SAVED_DN_REG " \\n[dn]\n"
1832 ".ds " QUOTE_STRING_NAME "\n"
1833 ".ds " TRANSPARENT_STRING_NAME "\n"
1834 ".nr " SECTION_DIVERSION_FLAG_REG " 0\n"
1835 ".if \\n[.t]<=\\n[dn] \\{"
1838 ".nr " SUPPRESS_BOTTOM_REG " 1\n"
1840 ".nr " SUPPRESS_BOTTOM_REG " 0\n"
1843 ".if \\n[.t]<=\\n[" SAVED_DN_REG "] "
1844 /* Since we turn off traps, it won't get into an infinite loop
1845 when we try and print it; it will just go off the bottom of the
1847 ".tm warning: page \\n%: table text block will not fit on one page\n"
1849 ".if \\n[ln] .nm \\n[ln]\n"
1850 ".nr " ROW_MAX_LINE_REG " \\n[ln]\n"
1852 "." SECTION_DIVERSION_NAME "\n"
1855 ".rm " SECTION_DIVERSION_NAME "\n"
1863 ".nr " TABLE_DIVERSION_FLAG_REG " 0\n"
1864 ".de " TABLE_KEEP_MACRO_NAME "\n"
1865 ".if '\\n[.z]'' \\{"
1866 ".di " TABLE_DIVERSION_NAME "\n"
1867 ".nr " TABLE_DIVERSION_FLAG_REG " 1\n"
1870 ".de " TABLE_RELEASE_MACRO_NAME "\n"
1871 ".if \\n[" TABLE_DIVERSION_FLAG_REG "] \\{.br\n"
1873 ".nr " SAVED_DN_REG " \\n[dn]\n"
1874 ".ne \\n[dn]u+\\n[.V]u\n"
1875 ".ie \\n[.t]<=\\n[" SAVED_DN_REG "] "
1876 ".tm error: page \\n%: table will not fit on one page; use .TS H/.TH with a supporting macro package\n"
1881 ".if \\n[ln] .nm \\n[ln]\n"
1882 "." TABLE_DIVERSION_NAME "\n"
1884 ".rm " TABLE_DIVERSION_NAME "\n"
1886 ".if \\n[ln] \\{.nm\n"
1887 ".nr ln \\n[" ROW_MAX_LINE_REG "]\n"
1895 string block_width_reg(int r, int c)
1897 static char name[sizeof(BLOCK_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1898 sprintf(name, BLOCK_WIDTH_PREFIX "%d,%d", r, c);
1899 return string(name);
1902 string block_diversion_name(int r, int c)
1904 static char name[sizeof(BLOCK_DIVERSION_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1905 sprintf(name, BLOCK_DIVERSION_PREFIX "%d,%d", r, c);
1906 return string(name);
1909 string block_height_reg(int r, int c)
1911 static char name[sizeof(BLOCK_HEIGHT_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1912 sprintf(name, BLOCK_HEIGHT_PREFIX "%d,%d", r, c);
1913 return string(name);
1916 string span_width_reg(int start_col, int end_col)
1918 static char name[sizeof(SPAN_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1919 sprintf(name, SPAN_WIDTH_PREFIX "%d", start_col);
1920 if (end_col != start_col)
1921 sprintf(strchr(name, '\0'), ",%d", end_col);
1922 return string(name);
1925 string span_left_numeric_width_reg(int start_col, int end_col)
1927 static char name[sizeof(SPAN_LEFT_NUMERIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1928 sprintf(name, SPAN_LEFT_NUMERIC_WIDTH_PREFIX "%d", start_col);
1929 if (end_col != start_col)
1930 sprintf(strchr(name, '\0'), ",%d", end_col);
1931 return string(name);
1934 string span_right_numeric_width_reg(int start_col, int end_col)
1936 static char name[sizeof(SPAN_RIGHT_NUMERIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1937 sprintf(name, SPAN_RIGHT_NUMERIC_WIDTH_PREFIX "%d", start_col);
1938 if (end_col != start_col)
1939 sprintf(strchr(name, '\0'), ",%d", end_col);
1940 return string(name);
1943 string span_alphabetic_width_reg(int start_col, int end_col)
1945 static char name[sizeof(SPAN_ALPHABETIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1946 sprintf(name, SPAN_ALPHABETIC_WIDTH_PREFIX "%d", start_col);
1947 if (end_col != start_col)
1948 sprintf(strchr(name, '\0'), ",%d", end_col);
1949 return string(name);
1952 string column_separation_reg(int col)
1954 static char name[sizeof(COLUMN_SEPARATION_PREFIX)+INT_DIGITS];
1955 sprintf(name, COLUMN_SEPARATION_PREFIX "%d", col);
1956 return string(name);
1959 string row_start_reg(int row)
1961 static char name[sizeof(ROW_START_PREFIX)+INT_DIGITS];
1962 sprintf(name, ROW_START_PREFIX "%d", row);
1963 return string(name);
1966 string column_start_reg(int col)
1968 static char name[sizeof(COLUMN_START_PREFIX)+INT_DIGITS];
1969 sprintf(name, COLUMN_START_PREFIX "%d", col);
1970 return string(name);
1973 string column_end_reg(int col)
1975 static char name[sizeof(COLUMN_END_PREFIX)+INT_DIGITS];
1976 sprintf(name, COLUMN_END_PREFIX "%d", col);
1977 return string(name);
1980 string column_divide_reg(int col)
1982 static char name[sizeof(COLUMN_DIVIDE_PREFIX)+INT_DIGITS];
1983 sprintf(name, COLUMN_DIVIDE_PREFIX "%d", col);
1984 return string(name);
1987 string row_top_reg(int row)
1989 static char name[sizeof(ROW_TOP_PREFIX)+INT_DIGITS];
1990 sprintf(name, ROW_TOP_PREFIX "%d", row);
1991 return string(name);
1994 void init_span_reg(int start_col, int end_col)
1996 printfs(".nr %1 \\n(.H\n.nr %2 0\n.nr %3 0\n.nr %4 0\n",
1997 span_width_reg(start_col, end_col),
1998 span_alphabetic_width_reg(start_col, end_col),
1999 span_left_numeric_width_reg(start_col, end_col),
2000 span_right_numeric_width_reg(start_col, end_col));
2003 void compute_span_width(int start_col, int end_col)
2005 printfs(".nr %1 \\n[%1]>?(\\n[%2]+\\n[%3])\n"
2006 ".if \\n[%4] .nr %1 \\n[%1]>?(\\n[%4]+2n)\n",
2007 span_width_reg(start_col, end_col),
2008 span_left_numeric_width_reg(start_col, end_col),
2009 span_right_numeric_width_reg(start_col, end_col),
2010 span_alphabetic_width_reg(start_col, end_col));
2013 // Increase the widths of columns so that the width of any spanning entry
2014 // is not greater than the sum of the widths of the columns that it spans.
2015 // Ensure that the widths of columns remain equal.
2017 void table::divide_span(int start_col, int end_col)
2019 assert(end_col > start_col);
2020 printfs(".nr " NEEDED_REG " \\n[%1]-(\\n[%2]",
2021 span_width_reg(start_col, end_col),
2022 span_width_reg(start_col, start_col));
2024 for (i = start_col + 1; i <= end_col; i++) {
2025 // The column separation may shrink with the expand option.
2026 if (!(flags & EXPAND))
2027 printfs("+%1n", as_string(column_separation[i - 1]));
2028 printfs("+\\n[%1]", span_width_reg(i, i));
2031 printfs(".nr " NEEDED_REG " \\n[" NEEDED_REG "]/%1\n",
2032 as_string(end_col - start_col + 1));
2033 prints(".if \\n[" NEEDED_REG "] \\{");
2034 for (i = start_col; i <= end_col; i++)
2035 printfs(".nr %1 +\\n[" NEEDED_REG "]\n",
2036 span_width_reg(i, i));
2038 for (i = start_col; i <= end_col && !equal_flag; i++)
2039 if (equal[i] || expand[i])
2042 for (i = 0; i < ncolumns; i++)
2043 if (i < start_col || i > end_col)
2044 printfs(".nr %1 +\\n[" NEEDED_REG "]\n",
2045 span_width_reg(i, i));
2050 void table::sum_columns(int start_col, int end_col, int do_expand)
2052 assert(end_col > start_col);
2054 for (i = start_col; i <= end_col; i++)
2065 printfs(".nr %1 \\n[%2]",
2066 span_width_reg(start_col, end_col),
2067 span_width_reg(start_col, start_col));
2068 for (i = start_col + 1; i <= end_col; i++)
2069 printfs("+(%1*\\n[" SEPARATION_FACTOR_REG "])+\\n[%2]",
2070 as_string(column_separation[i - 1]),
2071 span_width_reg(i, i));
2075 horizontal_span::horizontal_span(int sc, int ec, horizontal_span *p)
2076 : next(p), start_col(sc), end_col(ec)
2080 void table::build_span_list()
2083 table_entry *p = entry_list;
2085 if (p->end_col != p->start_col) {
2087 for (q = span_list; q; q = q->next)
2088 if (q->start_col == p->start_col
2089 && q->end_col == p->end_col)
2092 span_list = new horizontal_span(p->start_col, p->end_col, span_list);
2096 // Now sort span_list primarily by order of end_row, and secondarily
2097 // by reverse order of start_row. This ensures that if we divide
2098 // spans using the order in span_list, we will get reasonable results.
2099 horizontal_span *unsorted = span_list;
2102 horizontal_span **pp;
2103 for (pp = &span_list; *pp; pp = &(*pp)->next)
2104 if (unsorted->end_col < (*pp)->end_col
2105 || (unsorted->end_col == (*pp)->end_col
2106 && (unsorted->start_col > (*pp)->start_col)))
2108 horizontal_span *tem = unsorted->next;
2109 unsorted->next = *pp;
2115 void table::compute_expand_width()
2118 int colcount = count_expand_columns();
2119 prints(".nr " EXPAND_REG " \\n[.l]-\\n[.i]");
2120 for (i = 0; i < ncolumns; i++)
2122 printfs("-\\n[%1]", span_width_reg(i, i));
2123 if (total_separation)
2124 printfs("-%1n", as_string(total_separation));
2126 prints(".if \\n[" EXPAND_REG "]<0 \\{\\\n");
2127 entry_list->set_location();
2128 if (!(flags & NOWARN)) {
2129 // protect ` and ' in warning message against eqn
2135 prints(".tm1 \"warning: file `\\n[.F]', around line \\n[.c]:\n"
2136 ".tm1 \" table wider than line width\n");
2142 prints(".nr " EXPAND_REG " 0\n");
2146 printfs(".nr " EXPAND_REG " \\n[" EXPAND_REG "]/%1\n",
2147 as_string(colcount));
2148 for (i = 0; i < ncolumns; i++)
2150 printfs(".nr %1 \\n[%1]>?\\n[" EXPAND_REG "]\n", span_width_reg(i, i));
2153 void table::compute_total_separation()
2155 if (flags & (ALLBOX | BOX | DOUBLEBOX))
2156 left_separation = right_separation = 1;
2158 for (int i = 0; i < nrows; i++) {
2159 if (vline[i][0] > 0)
2160 left_separation = 1;
2161 if (vline[i][ncolumns] > 0)
2162 right_separation = 1;
2165 total_separation = left_separation + right_separation;
2167 for (i = 0; i < ncolumns - 1; i++)
2168 total_separation += column_separation[i];
2171 void table::compute_separation_factor()
2173 // Don't let the separation factor be negative.
2174 prints(".nr " SEPARATION_FACTOR_REG " \\n[.l]-\\n[.i]");
2175 for (int i = 0; i < ncolumns; i++)
2176 printfs("-\\n[%1]", span_width_reg(i, i));
2177 printfs("/%1\n", as_string(total_separation));
2178 prints(".ie \\n[" SEPARATION_FACTOR_REG "]<=0 \\{\\\n");
2179 entry_list->set_location();
2180 if (!(flags & NOWARN)) {
2181 // protect ` and ' in warning message against eqn
2187 prints(".tm1 \"warning: file `\\n[.F]', around line \\n[.c]:\n"
2188 ".tm1 \" column separation set to zero\n"
2189 ".nr " SEPARATION_FACTOR_REG " 0\n");
2192 ".el .if \\n[" SEPARATION_FACTOR_REG "]<1n \\{\\\n");
2193 entry_list->set_location();
2194 if (!(flags & NOWARN)) {
2195 prints(".tm1 \"warning: file `\\n[.F]', around line \\n[.c]:\n"
2196 ".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 "." REPEATED_NM_SUS_MACRO " s\n"
2343 "\\v'" BODY_DEPTH "'"
2344 "\\s[\\n[" LINESIZE_REG "]]");
2346 prints("\\D'l |\\n[TW]u 0'");
2350 while (start_col < ncolumns
2351 && entry[r][start_col] != 0
2352 && entry[r][start_col]->start_row != r)
2355 for (end_col = start_col;
2357 && (entry[r][end_col] == 0
2358 || entry[r][end_col]->start_row == r);
2361 if (end_col <= start_col)
2363 printfs("\\h'|\\n[%1]u",
2364 column_divide_reg(start_col));
2365 if ((r > 0 && vline[r-1][start_col] == 2)
2366 || (r < nrows && vline[r][start_col] == 2))
2367 prints("-" HALF_DOUBLE_LINE_SEP);
2369 printfs("\\D'l |\\n[%1]u",
2370 column_divide_reg(end_col));
2371 if ((r > 0 && vline[r-1][end_col] == 2)
2372 || (r < nrows && vline[r][end_col] == 2))
2373 prints("+" HALF_DOUBLE_LINE_SEP);
2375 start_col = end_col;
2379 prints("." REPEATED_NM_SUS_MACRO " r\n"
2384 void table::print_double_hline(int r)
2386 prints(".vs " LINE_SEP "+" DOUBLE_LINE_SEP
2389 "." REPEATED_NM_SUS_MACRO " s\n"
2390 "\\v'" BODY_DEPTH "'"
2391 "\\s[\\n[" LINESIZE_REG "]]");
2393 prints("\\v'-" DOUBLE_LINE_SEP "'"
2394 "\\D'l |\\n[TW]u 0'"
2395 "\\v'" DOUBLE_LINE_SEP "'"
2397 "\\D'l |\\n[TW]u 0'");
2401 while (start_col < ncolumns
2402 && entry[r][start_col] != 0
2403 && entry[r][start_col]->start_row != r)
2406 for (end_col = start_col;
2408 && (entry[r][end_col] == 0
2409 || entry[r][end_col]->start_row == r);
2412 if (end_col <= start_col)
2414 const char *left_adjust = 0;
2415 if ((r > 0 && vline[r-1][start_col] == 2)
2416 || (r < nrows && vline[r][start_col] == 2))
2417 left_adjust = "-" HALF_DOUBLE_LINE_SEP;
2418 const char *right_adjust = 0;
2419 if ((r > 0 && vline[r-1][end_col] == 2)
2420 || (r < nrows && vline[r][end_col] == 2))
2421 right_adjust = "+" HALF_DOUBLE_LINE_SEP;
2422 printfs("\\v'-" DOUBLE_LINE_SEP "'"
2424 column_divide_reg(start_col));
2426 prints(left_adjust);
2428 printfs("\\D'l |\\n[%1]u",
2429 column_divide_reg(end_col));
2431 prints(right_adjust);
2433 printfs("\\v'" DOUBLE_LINE_SEP "'"
2435 column_divide_reg(start_col));
2437 prints(left_adjust);
2439 printfs("\\D'l |\\n[%1]u",
2440 column_divide_reg(end_col));
2442 prints(right_adjust);
2444 start_col = end_col;
2448 "." REPEATED_NM_SUS_MACRO " r\n"
2453 void table::compute_vrule_top_adjust(int start_row, int col, string &result)
2455 if (row_is_all_lines[start_row] && start_row < nrows - 1) {
2456 if (row_is_all_lines[start_row] == 2)
2457 result = LINE_SEP ">?\\n[.V]u" "+" DOUBLE_LINE_SEP;
2459 result = LINE_SEP ">?\\n[.V]u";
2466 for (stuff *p = stuff_list; p && p->row <= start_row; p = p->next)
2467 if (p->row == start_row
2468 && (p->is_single_line() || p->is_double_line()))
2473 table_entry *e = entry[start_row-1][col-1];
2474 if (e && e->start_row == e->end_row) {
2475 if (e->to_double_line_entry() != 0)
2477 else if (e->to_single_line_entry() != 0)
2482 if (col < ncolumns) {
2483 table_entry *e = entry[start_row-1][col];
2484 if (e && e->start_row == e->end_row) {
2485 if (e->to_double_line_entry() != 0)
2487 else if (e->to_single_line_entry() != 0)
2491 if (row_is_all_lines[start_row-1] == 0) {
2492 if (left > 0 || right > 0) {
2493 result += "-" BODY_DEPTH "-" BAR_HEIGHT;
2494 if ((left == 2 && right != 2) || (right == 2 && left != 2))
2495 result += "-" HALF_DOUBLE_LINE_SEP;
2496 else if (left == 2 && right == 2)
2497 result += "+" HALF_DOUBLE_LINE_SEP;
2500 else if (row_is_all_lines[start_row-1] == 2) {
2501 if ((left == 2 && right != 2) || (right == 2 && left != 2))
2502 result += "-" DOUBLE_LINE_SEP;
2503 else if (left == 1 || right == 1)
2504 result += "-" HALF_DOUBLE_LINE_SEP;
2508 void table::compute_vrule_bot_adjust(int end_row, int col, string &result)
2510 if (row_is_all_lines[end_row] && end_row > 0) {
2516 for (p = stuff_list; p && p->row < end_row + 1; p = p->next)
2518 if (p && p->row == end_row + 1 && p->is_double_line()) {
2519 result = "-" DOUBLE_LINE_SEP;
2522 if ((p != 0 && p->row == end_row + 1)
2523 || end_row == nrows - 1) {
2527 if (row_is_all_lines[end_row+1] == 1)
2529 else if (row_is_all_lines[end_row+1] == 2)
2530 result = LINE_SEP "+" DOUBLE_LINE_SEP;
2536 table_entry *e = entry[end_row+1][col-1];
2537 if (e && e->start_row == e->end_row) {
2538 if (e->to_double_line_entry() != 0)
2540 else if (e->to_single_line_entry() != 0)
2545 if (col < ncolumns) {
2546 table_entry *e = entry[end_row+1][col];
2547 if (e && e->start_row == e->end_row) {
2548 if (e->to_double_line_entry() != 0)
2550 else if (e->to_single_line_entry() != 0)
2554 if (row_is_all_lines[end_row+1] == 0) {
2555 if (left > 0 || right > 0) {
2556 result = "1v-" BODY_DEPTH "-" BAR_HEIGHT;
2557 if ((left == 2 && right != 2) || (right == 2 && left != 2))
2558 result += "+" HALF_DOUBLE_LINE_SEP;
2559 else if (left == 2 && right == 2)
2560 result += "-" HALF_DOUBLE_LINE_SEP;
2563 else if (row_is_all_lines[end_row+1] == 2) {
2564 if (left == 2 && right == 2)
2565 result += "-" DOUBLE_LINE_SEP;
2566 else if (left != 2 && right != 2 && (left == 1 || right == 1))
2567 result += "-" HALF_DOUBLE_LINE_SEP;
2571 void table::add_vertical_rule(int start_row, int end_row,
2572 int col, int is_double)
2574 vrule_list = new vertical_rule(start_row, end_row, col, is_double,
2576 compute_vrule_top_adjust(start_row, col, vrule_list->top_adjust);
2577 compute_vrule_bot_adjust(end_row, col, vrule_list->bot_adjust);
2580 void table::build_vrule_list()
2583 if (flags & ALLBOX) {
2584 for (col = 1; col < ncolumns; col++) {
2587 while (start_row < nrows && vline_spanned(start_row, col))
2589 if (start_row >= nrows)
2591 int end_row = start_row;
2592 while (end_row < nrows && !vline_spanned(end_row, col))
2595 add_vertical_rule(start_row, end_row, col, 0);
2596 start_row = end_row + 1;
2600 if (flags & (BOX | ALLBOX | DOUBLEBOX)) {
2601 add_vertical_rule(0, nrows - 1, 0, 0);
2602 add_vertical_rule(0, nrows - 1, ncolumns, 0);
2604 for (int end_row = 0; end_row < nrows; end_row++)
2605 for (col = 0; col < ncolumns+1; col++)
2606 if (vline[end_row][col] > 0
2607 && !vline_spanned(end_row, col)
2608 && (end_row == nrows - 1
2609 || vline[end_row+1][col] != vline[end_row][col]
2610 || vline_spanned(end_row+1, col))) {
2612 for (start_row = end_row - 1;
2614 && vline[start_row][col] == vline[end_row][col]
2615 && !vline_spanned(start_row, col);
2619 add_vertical_rule(start_row, end_row, col, vline[end_row][col] > 1);
2621 for (vertical_rule *p = vrule_list; p; p = p->next)
2623 for (int r = p->start_row; r <= p->end_row; r++) {
2624 if (p->col > 0 && entry[r][p->col-1] != 0
2625 && entry[r][p->col-1]->end_col == p->col-1) {
2626 int is_corner = r == p->start_row || r == p->end_row;
2627 entry[r][p->col-1]->note_double_vrule_on_right(is_corner);
2629 if (p->col < ncolumns && entry[r][p->col] != 0
2630 && entry[r][p->col]->start_col == p->col) {
2631 int is_corner = r == p->start_row || r == p->end_row;
2632 entry[r][p->col]->note_double_vrule_on_left(is_corner);
2637 void table::define_bottom_macro()
2640 // protect # in macro name against eqn
2647 ".if !\\n[" SUPPRESS_BOTTOM_REG "] \\{"
2648 "." REPEATED_VPT_MACRO " 0\n"
2649 ".mk " SAVED_VERTICAL_POS_REG "\n");
2650 if (flags & (BOX | ALLBOX | DOUBLEBOX)) {
2651 prints(".if \\n[T.]&\\n[" NEED_BOTTOM_RULE_REG "] \\{");
2652 print_single_hline(0);
2655 prints("." REPEATED_NM_SUS_MACRO " s\n"
2657 for (vertical_rule *p = vrule_list; p; p = p->next)
2658 p->contribute_to_bottom_macro(this);
2659 if (flags & DOUBLEBOX)
2660 prints(".if \\n[T.] \\{.vs " DOUBLE_LINE_SEP ">?\\n[.V]u\n"
2661 "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]"
2662 "\\D'l \\n[TW]u 0'\\s0\n"
2665 ".if \\n[" LAST_PASSED_ROW_REG "]>=0 "
2666 ".nr " TOP_REG " \\n[#T]-" DOUBLE_LINE_SEP "\n"
2668 "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]"
2669 "\\D'l 0 |\\n[" TOP_REG "]u-1v'\\s0\n"
2671 "\\v'" BODY_DEPTH "'\\h'|\\n[TW]u'\\s[\\n[" LINESIZE_REG "]]"
2672 "\\D'l 0 |\\n[" TOP_REG "]u-1v'\\s0\n");
2673 prints("." REPEATED_NM_SUS_MACRO " r\n"
2675 prints(".nr " LAST_PASSED_ROW_REG " \\n[" CURRENT_ROW_REG "]\n"
2676 ".sp |\\n[" SAVED_VERTICAL_POS_REG "]u\n"
2677 "." REPEATED_VPT_MACRO " 1\n"
2688 // is the vertical line before column c in row r horizontally spanned?
2690 int table::vline_spanned(int r, int c)
2692 assert(r >= 0 && r < nrows && c >= 0 && c < ncolumns + 1);
2693 return (c != 0 && c != ncolumns && entry[r][c] != 0
2694 && entry[r][c]->start_col != c
2695 // horizontally spanning lines don't count
2696 && entry[r][c]->to_double_line_entry() == 0
2697 && entry[r][c]->to_single_line_entry() == 0);
2700 int table::row_begins_section(int r)
2702 assert(r >= 0 && r < nrows);
2703 for (int i = 0; i < ncolumns; i++)
2704 if (entry[r][i] && entry[r][i]->start_row != r)
2709 int table::row_ends_section(int r)
2711 assert(r >= 0 && r < nrows);
2712 for (int i = 0; i < ncolumns; i++)
2713 if (entry[r][i] && entry[r][i]->end_row != r)
2718 void table::do_row(int r)
2720 if (!(flags & NOKEEP) && row_begins_section(r))
2721 prints("." KEEP_MACRO_NAME "\n");
2724 for (p = stuff_list; p && p->row < r; p = p->next)
2726 for (stuff *p1 = p; p1 && p1->row == r; p1 = p1->next)
2727 if (!p1->printed && (p1->is_single_line() || p1->is_double_line())) {
2731 if (!had_line && !row_is_all_lines[r])
2732 printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r));
2734 for (; p && p->row == r; p = p->next)
2737 if (!had_line && (p->is_single_line() || p->is_double_line())) {
2738 printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r));
2742 // change the row *after* printing the stuff list (which might contain .TH)
2743 printfs("\\*[" TRANSPARENT_STRING_NAME "].nr " CURRENT_ROW_REG " %1\n",
2745 if (!had_line && row_is_all_lines[r])
2746 printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r));
2747 // we might have had a .TH, for example, since we last tried
2748 if (!(flags & NOKEEP) && row_begins_section(r))
2749 prints("." KEEP_MACRO_NAME "\n");
2750 prints("." REPEATED_NM_SET_MACRO " d\n"
2751 ".nr " ROW_MAX_LINE_REG " \\n[ln]\n");
2752 printfs(".mk %1\n", row_start_reg(r));
2753 prints(".mk " BOTTOM_REG "\n"
2754 "." REPEATED_VPT_MACRO " 0\n");
2756 int row_is_blank = 1;
2757 int first_start_row = r;
2758 for (c = 0; c < ncolumns; c++) {
2759 table_entry *e = entry[r][c];
2761 if (e->end_row == r) {
2763 if (e->start_row < first_start_row)
2764 first_start_row = e->start_row;
2771 prints(".nr " BOTTOM_REG " +1v\n");
2772 if (row_is_all_lines[r]) {
2773 prints(".vs " LINE_SEP);
2774 if (row_is_all_lines[r] == 2)
2775 prints("+" DOUBLE_LINE_SEP);
2776 prints(">?\\n[.V]u\n.ls 1\n");
2778 prints("\\v'" BODY_DEPTH);
2779 if (row_is_all_lines[r] == 2)
2780 prints("-" HALF_DOUBLE_LINE_SEP);
2782 for (c = 0; c < ncolumns; c++) {
2783 table_entry *e = entry[r][c];
2785 if (e->end_row == e->start_row)
2786 e->to_simple_entry()->simple_print(1);
2793 prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n");
2794 printfs(".sp |\\n[%1]u\n", row_start_reg(r));
2796 for (int i = row_is_all_lines[r] ? r - 1 : r;
2797 i >= first_start_row;
2799 simple_entry *first = 0;
2800 for (c = 0; c < ncolumns; c++) {
2801 table_entry *e = entry[r][c];
2803 if (e->end_row == r && e->start_row == i) {
2804 simple_entry *simple = e->to_simple_entry();
2818 first->position_vertically();
2819 first->set_location();
2821 first->simple_print(0);
2822 for (c = first->end_col + 1; c < ncolumns; c++) {
2823 table_entry *e = entry[r][c];
2825 if (e->end_row == r && e->start_row == i) {
2826 simple_entry *simple = e->to_simple_entry();
2828 if (e->end_row != e->start_row) {
2830 simple->position_vertically();
2833 simple->simple_print(0);
2840 prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n");
2841 printfs(".sp |\\n[%1]u\n", row_start_reg(r));
2844 for (c = 0; c < ncolumns; c++) {
2845 table_entry *e = entry[r][c];
2847 if (e->end_row == r && e->to_simple_entry() == 0) {
2848 prints("." REPEATED_NM_SET_MACRO " s\n");
2849 e->position_vertically();
2851 prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n");
2852 printfs(".sp |\\n[%1]u\n", row_start_reg(r));
2857 prints("." REPEATED_NM_SET_MACRO " m\n"
2858 "." REPEATED_VPT_MACRO " 1\n"
2859 ".sp |\\n[" BOTTOM_REG "]u\n"
2860 "\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG " 1\n");
2861 if (r != nrows - 1 && (flags & ALLBOX)) {
2862 print_single_hline(r + 1);
2863 prints("\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG " 0\n");
2865 if (r != nrows - 1) {
2866 if (p && p->row == r + 1
2867 && (p->is_single_line() || p->is_double_line())) {
2869 prints("\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG
2872 int printed_one = 0;
2873 for (vertical_rule *vr = vrule_list; vr; vr = vr->next)
2874 if (vr->end_row == r) {
2876 prints("." REPEATED_VPT_MACRO " 0\n");
2882 prints("." REPEATED_VPT_MACRO " 1\n");
2883 if (!(flags & NOKEEP) && row_ends_section(r))
2884 prints("." RELEASE_MACRO_NAME "\n");
2886 prints(".if \\n[ln] .nr ln \\n[" ROW_MAX_LINE_REG "]\n");
2889 void table::do_top()
2891 prints(".fc \002\003\n");
2892 if (!(flags & NOKEEP) && (flags & (BOX | DOUBLEBOX | ALLBOX)))
2893 prints("." TABLE_KEEP_MACRO_NAME "\n");
2894 if (flags & DOUBLEBOX) {
2895 prints("." REPEATED_NM_SUS_MACRO " s\n"
2897 ".vs " LINE_SEP ">?\\n[.V]u\n"
2898 "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]\\D'l \\n[TW]u 0'\\s0\n"
2900 "." REPEATED_MARK_MACRO " " TOP_REG "\n"
2901 ".vs " DOUBLE_LINE_SEP ">?\\n[.V]u\n");
2902 printfs("\\v'" BODY_DEPTH "'"
2903 "\\s[\\n[" LINESIZE_REG "]]"
2905 "\\D'l |\\n[%2]u 0'"
2908 column_divide_reg(0),
2909 column_divide_reg(ncolumns));
2910 prints("." REPEATED_NM_SUS_MACRO " r\n"
2914 else if (flags & (ALLBOX | BOX)) {
2915 print_single_hline(0);
2917 //printfs(".mk %1\n", row_top_reg(0));
2920 void table::do_bottom()
2922 // print stuff after last row
2923 for (stuff *p = stuff_list; p; p = p->next)
2924 if (p->row > nrows - 1)
2926 if (!(flags & NOKEEP))
2927 prints("." RELEASE_MACRO_NAME "\n");
2928 printfs(".mk %1\n", row_top_reg(nrows));
2929 prints(".nr " NEED_BOTTOM_RULE_REG " 1\n"
2931 // protect # in macro name against eqn
2943 if (!(flags & NOKEEP) && (flags & (BOX | DOUBLEBOX | ALLBOX)))
2944 prints("." TABLE_RELEASE_MACRO_NAME "\n");
2946 prints(".if \\n[ln] \\{.nm\n"
2947 ".nr ln \\n[" ROW_MAX_LINE_REG "]\n"
2949 if (flags & DOUBLEBOX)
2950 prints(".sp " DOUBLE_LINE_SEP "\n");
2951 prints("." RESET_MACRO_NAME "\n"
2953 ".cp \\n(" COMPATIBLE_REG "\n");
2956 int table::get_nrows()
2961 const char *last_filename = 0;
2963 void set_troff_location(const char *fn, int ln)
2965 if (!location_force_filename && last_filename != 0
2966 && strcmp(fn, last_filename) == 0)
2967 printfs(".lf %1\n", as_string(ln));
2969 printfs(".lf %1 %2\n", as_string(ln), fn);
2971 location_force_filename = 0;
2975 void printfs(const char *s, const string &arg1, const string &arg2,
2976 const string &arg3, const string &arg4, const string &arg5)
2980 while ((c = *s++) != '\0') {