diff options
Diffstat (limited to 'contrib/groff/tbl/table.cc')
-rw-r--r-- | contrib/groff/tbl/table.cc | 2778 |
1 files changed, 2778 insertions, 0 deletions
diff --git a/contrib/groff/tbl/table.cc b/contrib/groff/tbl/table.cc new file mode 100644 index 000000000000..160ff9012558 --- /dev/null +++ b/contrib/groff/tbl/table.cc @@ -0,0 +1,2778 @@ +// -*- C++ -*- +/* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc. + Written by James Clark (jjc@jclark.com) + +This file is part of groff. + +groff is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2, or (at your option) any later +version. + +groff is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with groff; see the file COPYING. If not, write to the Free Software +Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "table.h" + +#define BAR_HEIGHT ".25m" +#define DOUBLE_LINE_SEP "2p" +#define HALF_DOUBLE_LINE_SEP "1p" +#define LINE_SEP "2p" +#define BODY_DEPTH ".25m" + +const int DEFAULT_COLUMN_SEPARATION = 3; + +#define DELIMITER_CHAR "\\[tbl]" +#define PREFIX "3" +#define SEPARATION_FACTOR_REG PREFIX "sep" +#define BOTTOM_REG PREFIX "bot" +#define RESET_MACRO_NAME PREFIX "init" +#define LINESIZE_REG PREFIX "lps" +#define TOP_REG PREFIX "top" +#define CURRENT_ROW_REG PREFIX "crow" +#define LAST_PASSED_ROW_REG PREFIX "passed" +#define TRANSPARENT_STRING_NAME PREFIX "trans" +#define QUOTE_STRING_NAME PREFIX "quote" +#define SECTION_DIVERSION_NAME PREFIX "section" +#define SECTION_DIVERSION_FLAG_REG PREFIX "sflag" +#define SAVED_VERTICAL_POS_REG PREFIX "vert" +#define NEED_BOTTOM_RULE_REG PREFIX "brule" +#define KEEP_MACRO_NAME PREFIX "keep" +#define RELEASE_MACRO_NAME PREFIX "release" +#define SAVED_FONT_REG PREFIX "fnt" +#define SAVED_SIZE_REG PREFIX "sz" +#define SAVED_FILL_REG PREFIX "fll" +#define SAVED_INDENT_REG PREFIX "ind" +#define SAVED_CENTER_REG PREFIX "cent" +#define TABLE_DIVERSION_NAME PREFIX "table" +#define TABLE_DIVERSION_FLAG_REG PREFIX "tflag" +#define TABLE_KEEP_MACRO_NAME PREFIX "tkeep" +#define TABLE_RELEASE_MACRO_NAME PREFIX "trelease" +#define NEEDED_REG PREFIX "needed" +#define REPEATED_MARK_MACRO PREFIX "rmk" +#define REPEATED_VPT_MACRO PREFIX "rvpt" +#define SUPPRESS_BOTTOM_REG PREFIX "supbot" +#define SAVED_DN_REG PREFIX "dn" + +// this must be one character +#define COMPATIBLE_REG PREFIX "c" + +#define BLOCK_WIDTH_PREFIX PREFIX "tbw" +#define BLOCK_DIVERSION_PREFIX PREFIX "tbd" +#define BLOCK_HEIGHT_PREFIX PREFIX "tbh" +#define SPAN_WIDTH_PREFIX PREFIX "w" +#define SPAN_LEFT_NUMERIC_WIDTH_PREFIX PREFIX "lnw" +#define SPAN_RIGHT_NUMERIC_WIDTH_PREFIX PREFIX "rnw" +#define SPAN_ALPHABETIC_WIDTH_PREFIX PREFIX "aw" +#define COLUMN_SEPARATION_PREFIX PREFIX "cs" +#define ROW_START_PREFIX PREFIX "rs" +#define COLUMN_START_PREFIX PREFIX "cl" +#define COLUMN_END_PREFIX PREFIX "ce" +#define COLUMN_DIVIDE_PREFIX PREFIX "cd" +#define ROW_TOP_PREFIX PREFIX "rt" + +string block_width_reg(int r, int c); +string block_diversion_name(int r, int c); +string block_height_reg(int r, int c); +string span_width_reg(int start_col, int end_col); +string span_left_numeric_width_reg(int start_col, int end_col); +string span_right_numeric_width_reg(int start_col, int end_col); +string span_alphabetic_width_reg(int start_col, int end_col); +string column_separation_reg(int col); +string row_start_reg(int r); +string column_start_reg(int c); +string column_end_reg(int c); +string column_divide_reg(int c); +string row_top_reg(int r); + +void set_inline_modifier(const entry_modifier *); +void restore_inline_modifier(const entry_modifier *m); +void set_modifier(const entry_modifier *); +int find_decimal_point(const char *s, char decimal_point_char, + const char *delim); + +string an_empty_string; +int location_force_filename = 0; + +void printfs(const char *, + const string &arg1 = an_empty_string, + const string &arg2 = an_empty_string, + const string &arg3 = an_empty_string, + const string &arg4 = an_empty_string, + const string &arg5 = an_empty_string); + +void prints(const string &); + +inline void prints(char c) +{ + putchar(c); +} + +inline void prints(const char *s) +{ + fputs(s, stdout); +} + +void prints(const string &s) +{ + if (!s.empty()) + fwrite(s.contents(), 1, s.length(), stdout); +} + +struct horizontal_span { + horizontal_span *next; + short start_col; + short end_col; + horizontal_span(int, int, horizontal_span *); +}; + +struct single_line_entry; +struct double_line_entry; +struct simple_entry; + +class table_entry { +friend class table; + table_entry *next; + int input_lineno; + const char *input_filename; +protected: + int start_row; + int end_row; + short start_col; + short end_col; + const entry_modifier *mod; +public: + void set_location(); + table_entry(const entry_modifier *); + virtual ~table_entry(); + virtual int divert(int ncols, const string *mw, int *sep); + virtual void do_width(); + virtual void do_depth(); + virtual void print() = 0; + virtual void position_vertically() = 0; + virtual single_line_entry *to_single_line_entry(); + virtual double_line_entry *to_double_line_entry(); + virtual simple_entry *to_simple_entry(); + virtual int line_type(); + virtual void note_double_vrule_on_right(int); + virtual void note_double_vrule_on_left(int); +}; + +class simple_entry : public table_entry { +public: + simple_entry(const entry_modifier *); + void print(); + void position_vertically(); + simple_entry *to_simple_entry(); + virtual void add_tab(); + virtual void simple_print(int); +}; + +class empty_entry : public simple_entry { +public: + empty_entry(const entry_modifier *); + int line_type(); +}; + +class text_entry : public simple_entry { +protected: + char *contents; + void print_contents(); +public: + text_entry(char *, const entry_modifier *); + ~text_entry(); +}; + +void text_entry::print_contents() +{ + set_inline_modifier(mod); + prints(contents); + restore_inline_modifier(mod); +} + +class repeated_char_entry : public text_entry { +public: + repeated_char_entry(char *s, const entry_modifier *m); + void simple_print(int); +}; + +class simple_text_entry : public text_entry { +public: + simple_text_entry(char *s, const entry_modifier *m); + void do_width(); +}; + +class left_text_entry : public simple_text_entry { +public: + left_text_entry(char *s, const entry_modifier *m); + void simple_print(int); + void add_tab(); +}; + +class right_text_entry : public simple_text_entry { +public: + right_text_entry(char *s, const entry_modifier *m); + void simple_print(int); + void add_tab(); +}; + +class center_text_entry : public simple_text_entry { +public: + center_text_entry(char *s, const entry_modifier *m); + void simple_print(int); + void add_tab(); +}; + +class numeric_text_entry : public text_entry { + int dot_pos; +public: + numeric_text_entry(char *s, const entry_modifier *m, int pos); + void do_width(); + void simple_print(int); +}; + +class alphabetic_text_entry : public text_entry { +public: + alphabetic_text_entry(char *s, const entry_modifier *m); + void do_width(); + void simple_print(int); + void add_tab(); +}; + +class line_entry : public simple_entry { +protected: + char double_vrule_on_right; + char double_vrule_on_left; +public: + line_entry(const entry_modifier *); + void note_double_vrule_on_right(int); + void note_double_vrule_on_left(int); + void simple_print(int) = 0; +}; + +class single_line_entry : public line_entry { +public: + single_line_entry(const entry_modifier *m); + void simple_print(int); + single_line_entry *to_single_line_entry(); + int line_type(); +}; + +class double_line_entry : public line_entry { +public: + double_line_entry(const entry_modifier *m); + void simple_print(int); + double_line_entry *to_double_line_entry(); + int line_type(); +}; + +class short_line_entry : public simple_entry { +public: + short_line_entry(const entry_modifier *m); + void simple_print(int); + int line_type(); +}; + +class short_double_line_entry : public simple_entry { +public: + short_double_line_entry(const entry_modifier *m); + void simple_print(int); + int line_type(); +}; + +class block_entry : public table_entry { + char *contents; +protected: + void do_divert(int alphabetic, int ncols, const string *mw, int *sep); +public: + block_entry(char *s, const entry_modifier *m); + ~block_entry(); + int divert(int ncols, const string *mw, int *sep); + void do_width(); + void do_depth(); + void position_vertically(); + void print() = 0; +}; + +class left_block_entry : public block_entry { +public: + left_block_entry(char *s, const entry_modifier *m); + void print(); +}; + +class right_block_entry : public block_entry { +public: + right_block_entry(char *s, const entry_modifier *m); + void print(); +}; + +class center_block_entry : public block_entry { +public: + center_block_entry(char *s, const entry_modifier *m); + void print(); +}; + +class alphabetic_block_entry : public block_entry { +public: + alphabetic_block_entry(char *s, const entry_modifier *m); + void print(); + int divert(int ncols, const string *mw, int *sep); +}; + +table_entry::table_entry(const entry_modifier *m) +: next(0), start_row(-1), end_row(-1), start_col(-1), end_col(-1), mod(m), + input_lineno(-1), input_filename(0) +{ +} + +table_entry::~table_entry() +{ +} + +int table_entry::divert(int, const string *, int *) +{ + return 0; +} + +void table_entry::do_width() +{ +} + +single_line_entry *table_entry::to_single_line_entry() +{ + return 0; +} + +double_line_entry *table_entry::to_double_line_entry() +{ + return 0; +} + +simple_entry *table_entry::to_simple_entry() +{ + return 0; +} + +void table_entry::do_depth() +{ +} + +void table_entry::set_location() +{ + set_troff_location(input_filename, input_lineno); +} + +int table_entry::line_type() +{ + return -1; +} + +void table_entry::note_double_vrule_on_right(int) +{ +} + +void table_entry::note_double_vrule_on_left(int) +{ +} + +simple_entry::simple_entry(const entry_modifier *m) : table_entry(m) +{ +} + +void simple_entry::add_tab() +{ + // do nothing +} + +void simple_entry::simple_print(int) +{ + // do nothing +} + +void simple_entry::position_vertically() +{ + if (start_row != end_row) + switch (mod->vertical_alignment) { + case entry_modifier::TOP: + printfs(".sp |\\n[%1]u\n", row_start_reg(start_row)); + break; + case entry_modifier::CENTER: + // Peform the motion in two stages so that the center is rounded + // vertically upwards even if net vertical motion is upwards. + printfs(".sp |\\n[%1]u\n", row_start_reg(start_row)); + printfs(".sp \\n[" BOTTOM_REG "]u-\\n[%1]u-1v/2u\n", + row_start_reg(start_row)); + break; + case entry_modifier::BOTTOM: + printfs(".sp |\\n[%1]u+\\n[" BOTTOM_REG "]u-\\n[%1]u-1v\n", + row_start_reg(start_row)); + break; + default: + assert(0); + } +} + +void simple_entry::print() +{ + prints(".ta"); + add_tab(); + prints('\n'); + set_location(); + prints("\\&"); + simple_print(0); + prints('\n'); +} + +simple_entry *simple_entry::to_simple_entry() +{ + return this; +} + +empty_entry::empty_entry(const entry_modifier *m) +: simple_entry(m) +{ +} + +int empty_entry::line_type() +{ + return 0; +} + +text_entry::text_entry(char *s, const entry_modifier *m) +: contents(s), simple_entry(m) +{ +} + +text_entry::~text_entry() +{ + a_delete contents; +} + + +repeated_char_entry::repeated_char_entry(char *s, const entry_modifier *m) +: text_entry(s, m) +{ +} + +void repeated_char_entry::simple_print(int) +{ + printfs("\\h'|\\n[%1]u'", column_start_reg(start_col)); + set_inline_modifier(mod); + printfs("\\l" DELIMITER_CHAR "\\n[%1]u\\&", + span_width_reg(start_col, end_col)); + prints(contents); + prints(DELIMITER_CHAR); + restore_inline_modifier(mod); +} + +simple_text_entry::simple_text_entry(char *s, const entry_modifier *m) +: text_entry(s, m) +{ +} + +void simple_text_entry::do_width() +{ + set_location(); + printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR, + span_width_reg(start_col, end_col)); + print_contents(); + prints(DELIMITER_CHAR "\n"); +} + +left_text_entry::left_text_entry(char *s, const entry_modifier *m) +: simple_text_entry(s, m) +{ +} + +void left_text_entry::simple_print(int) +{ + printfs("\\h'|\\n[%1]u'", column_start_reg(start_col)); + print_contents(); +} + +// The only point of this is to make `\a' ``work'' as in Unix tbl. Grrr. + +void left_text_entry::add_tab() +{ + printfs(" \\n[%1]u", column_end_reg(end_col)); +} + +right_text_entry::right_text_entry(char *s, const entry_modifier *m) +: simple_text_entry(s, m) +{ +} + +void right_text_entry::simple_print(int) +{ + printfs("\\h'|\\n[%1]u'", column_start_reg(start_col)); + prints("\002\003"); + print_contents(); + prints("\002"); +} + +void right_text_entry::add_tab() +{ + printfs(" \\n[%1]u", column_end_reg(end_col)); +} + +center_text_entry::center_text_entry(char *s, const entry_modifier *m) +: simple_text_entry(s, m) +{ +} + +void center_text_entry::simple_print(int) +{ + printfs("\\h'|\\n[%1]u'", column_start_reg(start_col)); + prints("\002\003"); + print_contents(); + prints("\003\002"); +} + +void center_text_entry::add_tab() +{ + printfs(" \\n[%1]u", column_end_reg(end_col)); +} + +numeric_text_entry::numeric_text_entry(char *s, const entry_modifier *m, int pos) +: text_entry(s, m), dot_pos(pos) +{ +} + +void numeric_text_entry::do_width() +{ + if (dot_pos != 0) { + set_location(); + printfs(".nr %1 0\\w" DELIMITER_CHAR, + block_width_reg(start_row, start_col)); + set_inline_modifier(mod); + for (int i = 0; i < dot_pos; i++) + prints(contents[i]); + restore_inline_modifier(mod); + prints(DELIMITER_CHAR "\n"); + printfs(".nr %1 \\n[%1]>?\\n[%2]\n", + span_left_numeric_width_reg(start_col, end_col), + block_width_reg(start_row, start_col)); + } + else + printfs(".nr %1 0\n", block_width_reg(start_row, start_col)); + if (contents[dot_pos] != '\0') { + set_location(); + printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR, + span_right_numeric_width_reg(start_col, end_col)); + set_inline_modifier(mod); + prints(contents + dot_pos); + restore_inline_modifier(mod); + prints(DELIMITER_CHAR "\n"); + } +} + +void numeric_text_entry::simple_print(int) +{ + printfs("\\h'|(\\n[%1]u-\\n[%2]u-\\n[%3]u/2u+\\n[%2]u+\\n[%4]u-\\n[%5]u)'", + span_width_reg(start_col, end_col), + span_left_numeric_width_reg(start_col, end_col), + span_right_numeric_width_reg(start_col, end_col), + column_start_reg(start_col), + block_width_reg(start_row, start_col)); + print_contents(); +} + +alphabetic_text_entry::alphabetic_text_entry(char *s, const entry_modifier *m) +: text_entry(s, m) +{ +} + +void alphabetic_text_entry::do_width() +{ + set_location(); + printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR, + span_alphabetic_width_reg(start_col, end_col)); + print_contents(); + prints(DELIMITER_CHAR "\n"); +} + +void alphabetic_text_entry::simple_print(int) +{ + printfs("\\h'|\\n[%1]u'", column_start_reg(start_col)); + printfs("\\h'\\n[%1]u-\\n[%2]u/2u'", + span_width_reg(start_col, end_col), + span_alphabetic_width_reg(start_col, end_col)); + print_contents(); +} + +// The only point of this is to make `\a' ``work'' as in Unix tbl. Grrr. + +void alphabetic_text_entry::add_tab() +{ + printfs(" \\n[%1]u", column_end_reg(end_col)); +} + +block_entry::block_entry(char *s, const entry_modifier *m) +: table_entry(m), contents(s) +{ +} + +block_entry::~block_entry() +{ + a_delete contents; +} + +void block_entry::position_vertically() +{ + if (start_row != end_row) + switch(mod->vertical_alignment) { + case entry_modifier::TOP: + printfs(".sp |\\n[%1]u\n", row_start_reg(start_row)); + break; + case entry_modifier::CENTER: + // Peform the motion in two stages so that the center is rounded + // vertically upwards even if net vertical motion is upwards. + printfs(".sp |\\n[%1]u\n", row_start_reg(start_row)); + printfs(".sp \\n[" BOTTOM_REG "]u-\\n[%1]u-\\n[%2]u/2u\n", + row_start_reg(start_row), + block_height_reg(start_row, start_col)); + break; + case entry_modifier::BOTTOM: + printfs(".sp |\\n[%1]u+\\n[" BOTTOM_REG "]u-\\n[%1]u-\\n[%2]u\n", + row_start_reg(start_row), + block_height_reg(start_row, start_col)); + break; + default: + assert(0); + } + if (mod->stagger) + prints(".sp -.5v\n"); +} + +int block_entry::divert(int ncols, const string *mw, int *sep) +{ + do_divert(0, ncols, mw, sep); + return 1; +} + +void block_entry::do_divert(int alphabetic, int ncols, const string *mw, + int *sep) +{ + printfs(".di %1\n", block_diversion_name(start_row, start_col)); + prints(".if \\n[" SAVED_FILL_REG "] .fi\n" + ".in 0\n"); + prints(".ll "); + int i; + for (i = start_col; i <= end_col; i++) + if (mw[i].empty()) + break; + if (i > end_col) { + // Every column spanned by this entry has a minimum width. + for (int i = start_col; i <= end_col; i++) { + if (i > start_col) { + if (sep) + printfs("+%1n", as_string(sep[i - 1])); + prints('+'); + } + printfs("(n;%1)", mw[i]); + } + printfs(">?\\n[%1]u", span_width_reg(start_col, end_col)); + } + else + printfs("(u;\\n[%1]>?(\\n[.l]*%2/%3))", + span_width_reg(start_col, end_col), + as_string(end_col - start_col + 1), + as_string(ncols + 1)); + if (alphabetic) + prints("-2n"); + prints("\n"); + set_modifier(mod); + prints(".cp \\n(" COMPATIBLE_REG "\n"); + set_location(); + prints(contents); + prints(".br\n.di\n.cp 0\n"); + if (!mod->zero_width) { + if (alphabetic) { + printfs(".nr %1 \\n[%1]>?(\\n[dl]+2n)\n", + span_width_reg(start_col, end_col)); + printfs(".nr %1 \\n[%1]>?\\n[dl]\n", + span_alphabetic_width_reg(start_col, end_col)); + } + else + printfs(".nr %1 \\n[%1]>?\\n[dl]\n", span_width_reg(start_col, end_col)); + } + printfs(".nr %1 \\n[dn]\n", block_height_reg(start_row, start_col)); + printfs(".nr %1 \\n[dl]\n", block_width_reg(start_row, start_col)); + prints("." RESET_MACRO_NAME "\n" + ".in \\n[" SAVED_INDENT_REG "]u\n" + ".nf\n"); + // the block might have contained .lf commands + location_force_filename = 1; +} + +void block_entry::do_width() +{ + // do nothing; the action happens in divert +} + +void block_entry::do_depth() +{ + printfs(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?(\\n[%1]+\\n[%2])\n", + row_start_reg(start_row), + block_height_reg(start_row, start_col)); +} + +left_block_entry::left_block_entry(char *s, const entry_modifier *m) +: block_entry(s, m) +{ +} + +void left_block_entry::print() +{ + printfs(".in +\\n[%1]u\n", column_start_reg(start_col)); + printfs(".%1\n", block_diversion_name(start_row, start_col)); + prints(".in\n"); +} + + + +right_block_entry::right_block_entry(char *s, const entry_modifier *m) +: block_entry(s, m) +{ +} + +void right_block_entry::print() +{ + printfs(".in +\\n[%1]u+\\n[%2]u-\\n[%3]u\n", + column_start_reg(start_col), + span_width_reg(start_col, end_col), + block_width_reg(start_row, start_col)); + printfs(".%1\n", block_diversion_name(start_row, start_col)); + prints(".in\n"); +} + +center_block_entry::center_block_entry(char *s, const entry_modifier *m) +: block_entry(s, m) +{ +} + +void center_block_entry::print() +{ + printfs(".in +\\n[%1]u+(\\n[%2]u-\\n[%3]u/2u)\n", + column_start_reg(start_col), + span_width_reg(start_col, end_col), + block_width_reg(start_row, start_col)); + printfs(".%1\n", block_diversion_name(start_row, start_col)); + prints(".in\n"); +} + +alphabetic_block_entry::alphabetic_block_entry(char *s, + const entry_modifier *m) +: block_entry(s, m) +{ +} + +int alphabetic_block_entry::divert(int ncols, const string *mw, int *sep) +{ + do_divert(1, ncols, mw, sep); + return 1; +} + +void alphabetic_block_entry::print() +{ + printfs(".in +\\n[%1]u+(\\n[%2]u-\\n[%3]u/2u)\n", + column_start_reg(start_col), + span_width_reg(start_col, end_col), + span_alphabetic_width_reg(start_col, end_col)); + printfs(".%1\n", block_diversion_name(start_row, start_col)); + prints(".in\n"); +} + +line_entry::line_entry(const entry_modifier *m) +: simple_entry(m), double_vrule_on_right(0), double_vrule_on_left(0) +{ +} + +void line_entry::note_double_vrule_on_right(int is_corner) +{ + double_vrule_on_right = is_corner ? 1 : 2; +} + +void line_entry::note_double_vrule_on_left(int is_corner) +{ + double_vrule_on_left = is_corner ? 1 : 2; +} + + +single_line_entry::single_line_entry(const entry_modifier *m) +: line_entry(m) +{ +} + +int single_line_entry::line_type() +{ + return 1; +} + +void single_line_entry::simple_print(int dont_move) +{ + printfs("\\h'|\\n[%1]u", + column_divide_reg(start_col)); + if (double_vrule_on_left) { + prints(double_vrule_on_left == 1 ? "-" : "+"); + prints(HALF_DOUBLE_LINE_SEP); + } + prints("'"); + if (!dont_move) + prints("\\v'-" BAR_HEIGHT "'"); + printfs("\\s[\\n[" LINESIZE_REG "]]" "\\D'l |\\n[%1]u", + column_divide_reg(end_col+1)); + if (double_vrule_on_right) { + prints(double_vrule_on_left == 1 ? "+" : "-"); + prints(HALF_DOUBLE_LINE_SEP); + } + prints("0'\\s0"); + if (!dont_move) + prints("\\v'" BAR_HEIGHT "'"); +} + +single_line_entry *single_line_entry::to_single_line_entry() +{ + return this; +} + +double_line_entry::double_line_entry(const entry_modifier *m) +: line_entry(m) +{ +} + +int double_line_entry::line_type() +{ + return 2; +} + +void double_line_entry::simple_print(int dont_move) +{ + if (!dont_move) + prints("\\v'-" BAR_HEIGHT "'"); + printfs("\\h'|\\n[%1]u", + column_divide_reg(start_col)); + if (double_vrule_on_left) { + prints(double_vrule_on_left == 1 ? "-" : "+"); + prints(HALF_DOUBLE_LINE_SEP); + } + prints("'"); + printfs("\\v'-" HALF_DOUBLE_LINE_SEP "'" + "\\s[\\n[" LINESIZE_REG "]]" + "\\D'l |\\n[%1]u", + column_divide_reg(end_col+1)); + if (double_vrule_on_right) + prints("-" HALF_DOUBLE_LINE_SEP); + prints(" 0'"); + printfs("\\v'" DOUBLE_LINE_SEP "'" + "\\D'l |\\n[%1]u", + column_divide_reg(start_col)); + if (double_vrule_on_right) { + prints(double_vrule_on_left == 1 ? "+" : "-"); + prints(HALF_DOUBLE_LINE_SEP); + } + prints(" 0'"); + prints("\\s0" + "\\v'-" HALF_DOUBLE_LINE_SEP "'"); + if (!dont_move) + prints("\\v'" BAR_HEIGHT "'"); +} + +double_line_entry *double_line_entry::to_double_line_entry() +{ + return this; +} + +short_line_entry::short_line_entry(const entry_modifier *m) +: simple_entry(m) +{ +} + +int short_line_entry::line_type() +{ + return 1; +} + +void short_line_entry::simple_print(int dont_move) +{ + if (mod->stagger) + prints("\\v'-.5v'"); + if (!dont_move) + prints("\\v'-" BAR_HEIGHT "'"); + printfs("\\h'|\\n[%1]u'", column_start_reg(start_col)); + printfs("\\s[\\n[" LINESIZE_REG "]]" + "\\D'l \\n[%1]u 0'" + "\\s0", + span_width_reg(start_col, end_col)); + if (!dont_move) + prints("\\v'" BAR_HEIGHT "'"); + if (mod->stagger) + prints("\\v'.5v'"); +} + +short_double_line_entry::short_double_line_entry(const entry_modifier *m) +: simple_entry(m) +{ +} + +int short_double_line_entry::line_type() +{ + return 2; +} + +void short_double_line_entry::simple_print(int dont_move) +{ + if (mod->stagger) + prints("\\v'-.5v'"); + if (!dont_move) + prints("\\v'-" BAR_HEIGHT "'"); + printfs("\\h'|\\n[%2]u'" + "\\v'-" HALF_DOUBLE_LINE_SEP "'" + "\\s[\\n[" LINESIZE_REG "]]" + "\\D'l \\n[%1]u 0'" + "\\v'" DOUBLE_LINE_SEP "'" + "\\D'l |\\n[%2]u 0'" + "\\s0" + "\\v'-" HALF_DOUBLE_LINE_SEP "'", + span_width_reg(start_col, end_col), + column_start_reg(start_col)); + if (!dont_move) + prints("\\v'" BAR_HEIGHT "'"); + if (mod->stagger) + prints("\\v'.5v'"); +} + +void set_modifier(const entry_modifier *m) +{ + if (!m->font.empty()) + printfs(".ft %1\n", m->font); + if (m->point_size.val != 0) { + prints(".ps "); + if (m->point_size.inc > 0) + prints('+'); + else if (m->point_size.inc < 0) + prints('-'); + printfs("%1\n", as_string(m->point_size.val)); + } + if (m->vertical_spacing.val != 0) { + prints(".vs "); + if (m->vertical_spacing.inc > 0) + prints('+'); + else if (m->vertical_spacing.inc < 0) + prints('-'); + printfs("%1\n", as_string(m->vertical_spacing.val)); + } +} + +void set_inline_modifier(const entry_modifier *m) +{ + if (!m->font.empty()) + printfs("\\f[%1]", m->font); + if (m->point_size.val != 0) { + prints("\\s["); + if (m->point_size.inc > 0) + prints('+'); + else if (m->point_size.inc < 0) + prints('-'); + printfs("%1]", as_string(m->point_size.val)); + } + if (m->stagger) + prints("\\v'-.5v'"); +} + +void restore_inline_modifier(const entry_modifier *m) +{ + if (!m->font.empty()) + prints("\\f[\\n[" SAVED_FONT_REG "]]"); + if (m->point_size.val != 0) + prints("\\s[\\n[" SAVED_SIZE_REG "]]"); + if (m->stagger) + prints("\\v'.5v'"); +} + + +struct stuff { + stuff *next; + int row; // occurs before row `row' + char printed; // has it been printed? + + stuff(int); + virtual void print(table *) = 0; + virtual ~stuff(); + virtual int is_single_line() { return 0; }; + virtual int is_double_line() { return 0; }; +}; + +stuff::stuff(int r) : row(r), next(0), printed(0) +{ +} + +stuff::~stuff() +{ +} + +struct text_stuff : public stuff { + string contents; + const char *filename; + int lineno; + + text_stuff(const string &, int r, const char *fn, int ln); + ~text_stuff(); + void print(table *); +}; + + +text_stuff::text_stuff(const string &s, int r, const char *fn, int ln) +: contents(s), stuff(r), filename(fn), lineno(ln) +{ +} + +text_stuff::~text_stuff() +{ +} + +void text_stuff::print(table *) +{ + printed = 1; + prints(".cp \\n(" COMPATIBLE_REG "\n"); + set_troff_location(filename, lineno); + prints(contents); + prints(".cp 0\n"); + location_force_filename = 1; // it might have been a .lf command +} + +struct single_hline_stuff : public stuff { + single_hline_stuff(int r); + void print(table *); + int is_single_line(); +}; + +single_hline_stuff::single_hline_stuff(int r) : stuff(r) +{ +} + +void single_hline_stuff::print(table *tbl) +{ + printed = 1; + tbl->print_single_hline(row); +} + +int single_hline_stuff::is_single_line() +{ + return 1; +} + +struct double_hline_stuff : stuff { + double_hline_stuff(int r); + void print(table *); + int is_double_line(); +}; + +double_hline_stuff::double_hline_stuff(int r) : stuff(r) +{ +} + +void double_hline_stuff::print(table *tbl) +{ + printed = 1; + tbl->print_double_hline(row); +} + +int double_hline_stuff::is_double_line() +{ + return 1; +} + +struct vertical_rule { + vertical_rule *next; + int start_row; + int end_row; + short col; + char is_double; + string top_adjust; + string bot_adjust; + + vertical_rule(int sr, int er, int c, int dbl, vertical_rule *); + ~vertical_rule(); + void contribute_to_bottom_macro(table *); + void print(); +}; + +vertical_rule::vertical_rule(int sr, int er, int c, int dbl, vertical_rule *p) +: start_row(sr), end_row(er), col(c), is_double(dbl), next(p) +{ +} + +vertical_rule::~vertical_rule() +{ +} + +void vertical_rule::contribute_to_bottom_macro(table *tbl) +{ + printfs(".if \\n[" CURRENT_ROW_REG "]>=%1", + as_string(start_row)); + if (end_row != tbl->get_nrows() - 1) + printfs("&(\\n[" CURRENT_ROW_REG "]<%1)", + as_string(end_row)); + prints(" \\{"); + printfs(".if %1<=\\n[" LAST_PASSED_ROW_REG "] .nr %2 \\n[#T]\n", + as_string(start_row), + row_top_reg(start_row)); + const char *offset_table[3]; + if (is_double) { + offset_table[0] = "-" HALF_DOUBLE_LINE_SEP; + offset_table[1] = "+" HALF_DOUBLE_LINE_SEP; + offset_table[2] = 0; + } + else { + offset_table[0] = ""; + offset_table[1] = 0; + } + for (const char **offsetp = offset_table; *offsetp; offsetp++) { + prints(".sp -1\n" + "\\v'" BODY_DEPTH); + if (!bot_adjust.empty()) + printfs("+%1", bot_adjust); + prints("'"); + printfs("\\h'\\n[%1]u%3'\\s[\\n[" LINESIZE_REG "]]\\D'l 0 |\\n[%2]u-1v", + column_divide_reg(col), + row_top_reg(start_row), + *offsetp); + if (!bot_adjust.empty()) + printfs("-(%1)", bot_adjust); + // don't perform the top adjustment if the top is actually #T + if (!top_adjust.empty()) + printfs("+((%1)*(%2>\\n[" LAST_PASSED_ROW_REG "]))", + top_adjust, + as_string(start_row)); + prints("'\\s0\n"); + } + prints(".\\}\n"); +} + +void vertical_rule::print() +{ + printfs("\\*[" TRANSPARENT_STRING_NAME "]" + ".if %1<=\\*[" QUOTE_STRING_NAME "]\\n[" LAST_PASSED_ROW_REG "] " + ".nr %2 \\*[" QUOTE_STRING_NAME "]\\n[#T]\n", + as_string(start_row), + row_top_reg(start_row)); + const char *offset_table[3]; + if (is_double) { + offset_table[0] = "-" HALF_DOUBLE_LINE_SEP; + offset_table[1] = "+" HALF_DOUBLE_LINE_SEP; + offset_table[2] = 0; + } + else { + offset_table[0] = ""; + offset_table[1] = 0; + } + for (const char **offsetp = offset_table; *offsetp; offsetp++) { + prints("\\*[" TRANSPARENT_STRING_NAME "].sp -1\n" + "\\*[" TRANSPARENT_STRING_NAME "]\\v'" BODY_DEPTH); + if (!bot_adjust.empty()) + printfs("+%1", bot_adjust); + prints("'"); + printfs("\\h'\\n[%1]u%3'" + "\\s[\\n[" LINESIZE_REG "]]" + "\\D'l 0 |\\*[" QUOTE_STRING_NAME "]\\n[%2]u-1v", + column_divide_reg(col), + row_top_reg(start_row), + *offsetp); + if (!bot_adjust.empty()) + printfs("-(%1)", bot_adjust); + // don't perform the top adjustment if the top is actually #T + if (!top_adjust.empty()) + printfs("+((%1)*(%2>\\*[" QUOTE_STRING_NAME "]\\n[" + LAST_PASSED_ROW_REG "]))", + top_adjust, + as_string(start_row)); + prints("'" + "\\s0\n"); + } +} + +table::table(int nc, unsigned f, int ls, char dpc) +: ncolumns(nc), flags(f), linesize(ls), decimal_point_char(dpc), + nrows(0), allocated_rows(0), entry(0), entry_list(0), + entry_list_tailp(&entry_list), + left_separation(0), right_separation(0), stuff_list(0), vline(0), + vrule_list(0), row_is_all_lines(0), span_list(0) +{ + minimum_width = new string[ncolumns]; + column_separation = ncolumns > 1 ? new int[ncolumns - 1] : 0; + equal = new char[ncolumns]; + int i; + for (i = 0; i < ncolumns; i++) + equal[i] = 0; + for (i = 0; i < ncolumns-1; i++) + column_separation[i] = DEFAULT_COLUMN_SEPARATION; + delim[0] = delim[1] = '\0'; +} + +table::~table() +{ + for (int i = 0; i < nrows; i++) { + a_delete entry[i]; + a_delete vline[i]; + } + a_delete entry; + a_delete vline; + while (entry_list) { + table_entry *tem = entry_list; + entry_list = entry_list->next; + delete tem; + } + ad_delete(ncolumns) minimum_width; + a_delete column_separation; + a_delete equal; + while (stuff_list) { + stuff *tem = stuff_list; + stuff_list = stuff_list->next; + delete tem; + } + while (vrule_list) { + vertical_rule *tem = vrule_list; + vrule_list = vrule_list->next; + delete tem; + } + a_delete row_is_all_lines; + while (span_list) { + horizontal_span *tem = span_list; + span_list = span_list->next; + delete tem; + } +} + +void table::set_delim(char c1, char c2) +{ + delim[0] = c1; + delim[1] = c2; +} + +void table::set_minimum_width(int c, const string &w) +{ + assert(c >= 0 && c < ncolumns); + minimum_width[c] = w; +} + +void table::set_column_separation(int c, int n) +{ + assert(c >= 0 && c < ncolumns - 1); + column_separation[c] = n; +} + +void table::set_equal_column(int c) +{ + assert(c >= 0 && c < ncolumns); + equal[c] = 1; +} + +void table::add_stuff(stuff *p) +{ + stuff **pp; + for (pp = &stuff_list; *pp; pp = &(*pp)->next) + ; + *pp = p; +} + +void table::add_text_line(int r, const string &s, const char *filename, int lineno) +{ + add_stuff(new text_stuff(s, r, filename, lineno)); +} + +void table::add_single_hline(int r) +{ + add_stuff(new single_hline_stuff(r)); +} + +void table::add_double_hline(int r) +{ + add_stuff(new double_hline_stuff(r)); +} + +void table::allocate(int r) +{ + if (r >= nrows) { + typedef table_entry **PPtable_entry; // work around g++ 1.36.1 bug + if (r >= allocated_rows) { + if (allocated_rows == 0) { + allocated_rows = 16; + if (allocated_rows <= r) + allocated_rows = r + 1; + entry = new PPtable_entry[allocated_rows]; + vline = new char*[allocated_rows]; + } + else { + table_entry ***old_entry = entry; + int old_allocated_rows = allocated_rows; + allocated_rows *= 2; + if (allocated_rows <= r) + allocated_rows = r + 1; + entry = new PPtable_entry[allocated_rows]; + memcpy(entry, old_entry, sizeof(table_entry**)*old_allocated_rows); + a_delete old_entry; + char **old_vline = vline; + vline = new char*[allocated_rows]; + memcpy(vline, old_vline, sizeof(char*)*old_allocated_rows); + a_delete old_vline; + } + } + assert(allocated_rows > r); + while (nrows <= r) { + entry[nrows] = new table_entry*[ncolumns]; + int i; + for (i = 0; i < ncolumns; i++) + entry[nrows][i] = 0; + vline[nrows] = new char[ncolumns+1]; + for (i = 0; i < ncolumns+1; i++) + vline[nrows][i] = 0; + nrows++; + } + } +} + +void table::do_hspan(int r, int c) +{ + assert(r >= 0 && c >= 0 && r < nrows && c < ncolumns); + if (c == 0) { + error("first column cannot be horizontally spanned"); + return; + } + table_entry *e = entry[r][c]; + if (e) { + assert(e->start_row <= r && r <= e->end_row + && e->start_col <= c && c <= e->end_col + && e->end_row - e->start_row > 0 + && e->end_col - e->start_col > 0); + return; + } + e = entry[r][c-1]; + // e can be 0 if we had an empty entry or an error + if (e == 0) + return; + if (e->start_row != r) { + /* + l l + ^ s */ + error("impossible horizontal span at row %1, column %2", r + 1, c + 1); + } + else { + e->end_col = c; + entry[r][c] = e; + } +} + +void table::do_vspan(int r, int c) +{ + assert(r >= 0 && c >= 0 && r < nrows && c < ncolumns); + if (r == 0) { + error("first row cannot be vertically spanned"); + return; + } + table_entry *e = entry[r][c]; + if (e) { + assert(e->start_row <= r && r <= e->end_row + && e->start_col <= c && c <= e->end_col + && e->end_row - e->start_row > 0 + && e->end_col - e->start_col > 0); + return; + } + e = entry[r-1][c]; + // e can be 0 if we had an empty entry or an error + if (e == 0) + return; + if (e->start_col != c) { + /* l s + l ^ */ + error("impossible vertical span at row %1, column %2", r + 1, c + 1); + } + else { + for (int i = c; i <= e->end_col; i++) { + assert(entry[r][i] == 0); + entry[r][i] = e; + } + e->end_row = r; + } +} + +int find_decimal_point(const char *s, char decimal_point_char, + const char *delim) +{ + if (s == 0 || *s == '\0') + return -1; + const char *p; + int in_delim = 0; // is p within eqn delimiters? + // tbl recognises \& even within eqn delimiters; I don't + for (p = s; *p; p++) + if (in_delim) { + if (*p == delim[1]) + in_delim = 0; + } + else if (*p == delim[0]) + in_delim = 1; + else if (p[0] == '\\' && p[1] == '&') + return p - s; + int possible_pos = -1; + in_delim = 0; + for (p = s; *p; p++) + if (in_delim) { + if (*p == delim[1]) + in_delim = 0; + } + else if (*p == delim[0]) + in_delim = 1; + else if (p[0] == decimal_point_char && csdigit(p[1])) + possible_pos = p - s; + if (possible_pos >= 0) + return possible_pos; + in_delim = 0; + for (p = s; *p; p++) + if (in_delim) { + if (*p == delim[1]) + in_delim = 0; + } + else if (*p == delim[0]) + in_delim = 1; + else if (csdigit(*p)) + possible_pos = p + 1 - s; + return possible_pos; +} + +void table::add_entry(int r, int c, const string &str, const entry_format *f, + const char *fn, int ln) +{ + allocate(r); + table_entry *e = 0; + if (str == "\\_") { + e = new short_line_entry(f); + } + else if (str == "\\=") { + e = new short_double_line_entry(f); + } + else if (str == "_") { + single_line_entry *lefte; + if (c > 0 && entry[r][c-1] != 0 && + (lefte = entry[r][c-1]->to_single_line_entry()) != 0 + && lefte->start_row == r + && lefte->mod->stagger == f->stagger) { + lefte->end_col = c; + entry[r][c] = lefte; + } + else + e = new single_line_entry(f); + } + else if (str == "=") { + double_line_entry *lefte; + if (c > 0 && entry[r][c-1] != 0 && + (lefte = entry[r][c-1]->to_double_line_entry()) != 0 + && lefte->start_row == r + && lefte->mod->stagger == f->stagger) { + lefte->end_col = c; + entry[r][c] = lefte; + } + else + e = new double_line_entry(f); + } + else if (str == "\\^") { + do_vspan(r, c); + } + else if (str.length() > 2 && str[0] == '\\' && str[1] == 'R') { + if (str.search('\n') >= 0) + error_with_file_and_line(fn, ln, "bad repeated character"); + else { + char *s = str.substring(2, str.length() - 2).extract(); + e = new repeated_char_entry(s, f); + } + } + else { + int is_block = str.search('\n') >= 0; + char *s; + switch (f->type) { + case FORMAT_SPAN: + assert(str.empty()); + do_hspan(r, c); + break; + case FORMAT_LEFT: + if (!str.empty()) { + s = str.extract(); + if (is_block) + e = new left_block_entry(s, f); + else + e = new left_text_entry(s, f); + } + else + e = new empty_entry(f); + break; + case FORMAT_CENTER: + if (!str.empty()) { + s = str.extract(); + if (is_block) + e = new center_block_entry(s, f); + else + e = new center_text_entry(s, f); + } + else + e = new empty_entry(f); + break; + case FORMAT_RIGHT: + if (!str.empty()) { + s = str.extract(); + if (is_block) + e = new right_block_entry(s, f); + else + e = new right_text_entry(s, f); + } + else + e = new empty_entry(f); + break; + case FORMAT_NUMERIC: + if (!str.empty()) { + s = str.extract(); + if (is_block) { + error_with_file_and_line(fn, ln, "can't have numeric text block"); + e = new left_block_entry(s, f); + } + else { + int pos = find_decimal_point(s, decimal_point_char, delim); + if (pos < 0) + e = new center_text_entry(s, f); + else + e = new numeric_text_entry(s, f, pos); + } + } + else + e = new empty_entry(f); + break; + case FORMAT_ALPHABETIC: + if (!str.empty()) { + s = str.extract(); + if (is_block) + e = new alphabetic_block_entry(s, f); + else + e = new alphabetic_text_entry(s, f); + } + else + e = new empty_entry(f); + break; + case FORMAT_VSPAN: + do_vspan(r, c); + break; + case FORMAT_HLINE: + if (str.length() != 0) + error_with_file_and_line(fn, ln, + "non-empty data entry for `_' format ignored"); + e = new single_line_entry(f); + break; + case FORMAT_DOUBLE_HLINE: + if (str.length() != 0) + error_with_file_and_line(fn, ln, + "non-empty data entry for `=' format ignored"); + e = new double_line_entry(f); + break; + default: + assert(0); + } + } + if (e) { + table_entry *preve = entry[r][c]; + if (preve) { + /* c s + ^ l */ + error_with_file_and_line(fn, ln, "row %1, column %2 already spanned", + r + 1, c + 1); + delete e; + } + else { + e->input_lineno = ln; + e->input_filename = fn; + e->start_row = e->end_row = r; + e->start_col = e->end_col = c; + *entry_list_tailp = e; + entry_list_tailp = &e->next; + entry[r][c] = e; + } + } +} + +// add vertical lines for row r + +void table::add_vlines(int r, const char *v) +{ + allocate(r); + for (int i = 0; i < ncolumns+1; i++) + vline[r][i] = v[i]; +} + +void table::check() +{ + table_entry *p = entry_list; + int i, j; + while (p) { + for (i = p->start_row; i <= p->end_row; i++) + for (j = p->start_col; j <= p->end_col; j++) + assert(entry[i][j] == p); + p = p->next; + } +} + +void table::print() +{ + location_force_filename = 1; + check(); + init_output(); + determine_row_type(); + compute_widths(); + if (!(flags & CENTER)) + prints(".if \\n[" SAVED_CENTER_REG "] \\{"); + prints(".in +(u;\\n[.l]-\\n[.i]-\\n[TW]/2)\n" + ".nr " SAVED_INDENT_REG " \\n[.i]\n"); + if (!(flags & CENTER)) + prints(".\\}\n"); + build_vrule_list(); + define_bottom_macro(); + do_top(); + for (int i = 0; i < nrows; i++) + do_row(i); + do_bottom(); +} + +void table::determine_row_type() +{ + row_is_all_lines = new char[nrows]; + for (int i = 0; i < nrows; i++) { + int had_single = 0; + int had_double = 0; + int had_non_line = 0; + for (int c = 0; c < ncolumns; c++) { + table_entry *e = entry[i][c]; + if (e != 0) { + if (e->start_row == e->end_row) { + int t = e->line_type(); + switch (t) { + case -1: + had_non_line = 1; + break; + case 0: + // empty + break; + case 1: + had_single = 1; + break; + case 2: + had_double = 1; + break; + default: + assert(0); + } + if (had_non_line) + break; + } + c = e->end_col; + } + } + if (had_non_line) + row_is_all_lines[i] = 0; + else if (had_double) + row_is_all_lines[i] = 2; + else if (had_single) + row_is_all_lines[i] = 1; + else + row_is_all_lines[i] = 0; + } +} + + +void table::init_output() +{ + prints(".nr " COMPATIBLE_REG " \\n(.C\n" + ".cp 0\n"); + if (linesize > 0) + printfs(".nr " LINESIZE_REG " %1\n", as_string(linesize)); + else + prints(".nr " LINESIZE_REG " \\n[.s]\n"); + if (!(flags & CENTER)) + prints(".nr " SAVED_CENTER_REG " \\n[.ce]\n"); + prints(".de " RESET_MACRO_NAME "\n" + ".ft \\n[.f]\n" + ".ps \\n[.s]\n" + ".vs \\n[.v]u\n" + ".in \\n[.i]u\n" + ".ll \\n[.l]u\n" + ".ls \\n[.L]\n" + ".ad \\n[.j]\n" + ".ie \\n[.u] .fi\n" + ".el .nf\n" + ".ce \\n[.ce]\n" + "..\n" + ".nr " SAVED_INDENT_REG " \\n[.i]\n" + ".nr " SAVED_FONT_REG " \\n[.f]\n" + ".nr " SAVED_SIZE_REG " \\n[.s]\n" + ".nr " SAVED_FILL_REG " \\n[.u]\n" + ".nr T. 0\n" + ".nr " CURRENT_ROW_REG " 0-1\n" + ".nr " LAST_PASSED_ROW_REG " 0-1\n" + ".nr " SECTION_DIVERSION_FLAG_REG " 0\n" + ".ds " TRANSPARENT_STRING_NAME "\n" + ".ds " QUOTE_STRING_NAME "\n" + ".nr " NEED_BOTTOM_RULE_REG " 1\n" + ".nr " SUPPRESS_BOTTOM_REG " 0\n" + ".eo\n" + ".de " REPEATED_MARK_MACRO "\n" + ".mk \\$1\n" + ".if !'\\n(.z'' \\!." REPEATED_MARK_MACRO " \"\\$1\"\n" + "..\n" + ".de " REPEATED_VPT_MACRO "\n" + ".vpt \\$1\n" + ".if !'\\n(.z'' \\!." REPEATED_VPT_MACRO " \"\\$1\"\n" + "..\n"); + if (!(flags & NOKEEP)) + prints(".de " KEEP_MACRO_NAME "\n" + ".if '\\n[.z]'' \\{.ds " QUOTE_STRING_NAME " \\\\\n" + ".ds " TRANSPARENT_STRING_NAME " \\!\n" + ".di " SECTION_DIVERSION_NAME "\n" + ".nr " SECTION_DIVERSION_FLAG_REG " 1\n" + ".in 0\n" + ".\\}\n" + "..\n" + ".de " RELEASE_MACRO_NAME "\n" + ".if \\n[" SECTION_DIVERSION_FLAG_REG "] \\{" + ".di\n" + ".in \\n[" SAVED_INDENT_REG "]u\n" + ".nr " SAVED_DN_REG " \\n[dn]\n" + ".ds " QUOTE_STRING_NAME "\n" + ".ds " TRANSPARENT_STRING_NAME "\n" + ".nr " SECTION_DIVERSION_FLAG_REG " 0\n" + ".if \\n[.t]<=\\n[dn] \\{" + ".nr T. 1\n" + ".T#\n" + ".nr " SUPPRESS_BOTTOM_REG " 1\n" + ".sp \\n[.t]u\n" + ".nr " SUPPRESS_BOTTOM_REG " 0\n" + ".mk #T\n" + ".\\}\n" + ".if \\n[.t]<=\\n[" SAVED_DN_REG "] " + /* Since we turn off traps, it won't get into an infinite loop + when we try and print it; it will just go off the bottom of the + page. */ + ".tm warning: page \\n%: table text block will not fit on one page\n" + ".nf\n" + ".ls 1\n" + "." SECTION_DIVERSION_NAME "\n" + ".ls\n" + ".rm " SECTION_DIVERSION_NAME "\n" + ".\\}\n" + "..\n" + ".nr " TABLE_DIVERSION_FLAG_REG " 0\n" + ".de " TABLE_KEEP_MACRO_NAME "\n" + ".if '\\n[.z]'' \\{" + ".di " TABLE_DIVERSION_NAME "\n" + ".nr " TABLE_DIVERSION_FLAG_REG " 1\n" + ".\\}\n" + "..\n" + ".de " TABLE_RELEASE_MACRO_NAME "\n" + ".if \\n[" TABLE_DIVERSION_FLAG_REG "] \\{.br\n" + ".di\n" + ".nr " SAVED_DN_REG " \\n[dn]\n" + ".ne \\n[dn]u+\\n[.V]u\n" + ".ie \\n[.t]<=\\n[" SAVED_DN_REG "] " + ".tm error: page \\n%: table will not fit on one page; use .TS H/.TH with a supporting macro package\n" + ".el \\{" + ".in 0\n" + ".ls 1\n" + ".nf\n" + "." TABLE_DIVERSION_NAME "\n" + ".\\}\n" + ".rm " TABLE_DIVERSION_NAME "\n" + ".\\}\n" + "..\n"); + prints(".ec\n" + ".ce 0\n" + ".nf\n"); +} + +string block_width_reg(int r, int c) +{ + static char name[sizeof(BLOCK_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS]; + sprintf(name, BLOCK_WIDTH_PREFIX "%d,%d", r, c); + return string(name); +} + +string block_diversion_name(int r, int c) +{ + static char name[sizeof(BLOCK_DIVERSION_PREFIX)+INT_DIGITS+1+INT_DIGITS]; + sprintf(name, BLOCK_DIVERSION_PREFIX "%d,%d", r, c); + return string(name); +} + +string block_height_reg(int r, int c) +{ + static char name[sizeof(BLOCK_HEIGHT_PREFIX)+INT_DIGITS+1+INT_DIGITS]; + sprintf(name, BLOCK_HEIGHT_PREFIX "%d,%d", r, c); + return string(name); +} + +string span_width_reg(int start_col, int end_col) +{ + static char name[sizeof(SPAN_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS]; + sprintf(name, SPAN_WIDTH_PREFIX "%d", start_col); + if (end_col != start_col) + sprintf(strchr(name, '\0'), ",%d", end_col); + return string(name); +} + +string span_left_numeric_width_reg(int start_col, int end_col) +{ + static char name[sizeof(SPAN_LEFT_NUMERIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS]; + sprintf(name, SPAN_LEFT_NUMERIC_WIDTH_PREFIX "%d", start_col); + if (end_col != start_col) + sprintf(strchr(name, '\0'), ",%d", end_col); + return string(name); +} + +string span_right_numeric_width_reg(int start_col, int end_col) +{ + static char name[sizeof(SPAN_RIGHT_NUMERIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS]; + sprintf(name, SPAN_RIGHT_NUMERIC_WIDTH_PREFIX "%d", start_col); + if (end_col != start_col) + sprintf(strchr(name, '\0'), ",%d", end_col); + return string(name); +} + +string span_alphabetic_width_reg(int start_col, int end_col) +{ + static char name[sizeof(SPAN_ALPHABETIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS]; + sprintf(name, SPAN_ALPHABETIC_WIDTH_PREFIX "%d", start_col); + if (end_col != start_col) + sprintf(strchr(name, '\0'), ",%d", end_col); + return string(name); +} + + +string column_separation_reg(int col) +{ + static char name[sizeof(COLUMN_SEPARATION_PREFIX)+INT_DIGITS]; + sprintf(name, COLUMN_SEPARATION_PREFIX "%d", col); + return string(name); +} + +string row_start_reg(int row) +{ + static char name[sizeof(ROW_START_PREFIX)+INT_DIGITS]; + sprintf(name, ROW_START_PREFIX "%d", row); + return string(name); +} + +string column_start_reg(int col) +{ + static char name[sizeof(COLUMN_START_PREFIX)+INT_DIGITS]; + sprintf(name, COLUMN_START_PREFIX "%d", col); + return string(name); +} + +string column_end_reg(int col) +{ + static char name[sizeof(COLUMN_END_PREFIX)+INT_DIGITS]; + sprintf(name, COLUMN_END_PREFIX "%d", col); + return string(name); +} + +string column_divide_reg(int col) +{ + static char name[sizeof(COLUMN_DIVIDE_PREFIX)+INT_DIGITS]; + sprintf(name, COLUMN_DIVIDE_PREFIX "%d", col); + return string(name); +} + +string row_top_reg(int row) +{ + static char name[sizeof(ROW_TOP_PREFIX)+INT_DIGITS]; + sprintf(name, ROW_TOP_PREFIX "%d", row); + return string(name); +} + +void init_span_reg(int start_col, int end_col) +{ + printfs(".nr %1 \\n(.H\n.nr %2 0\n.nr %3 0\n.nr %4 0\n", + span_width_reg(start_col, end_col), + span_alphabetic_width_reg(start_col, end_col), + span_left_numeric_width_reg(start_col, end_col), + span_right_numeric_width_reg(start_col, end_col)); +} + +void compute_span_width(int start_col, int end_col) +{ + printfs(".nr %1 \\n[%1]>?(\\n[%2]+\\n[%3])\n" + ".if \\n[%4] .nr %1 \\n[%1]>?(\\n[%4]+2n)\n", + span_width_reg(start_col, end_col), + span_left_numeric_width_reg(start_col, end_col), + span_right_numeric_width_reg(start_col, end_col), + span_alphabetic_width_reg(start_col, end_col)); + +} + +// Increase the widths of columns so that the width of any spanning entry +// is no greater than the sum of the widths of the columns that it spans. +// Ensure that the widths of columns remain equal. + +void table::divide_span(int start_col, int end_col) +{ + assert(end_col > start_col); + printfs(".nr " NEEDED_REG " \\n[%1]-(\\n[%2]", + span_width_reg(start_col, end_col), + span_width_reg(start_col, start_col)); + int i; + for (i = start_col + 1; i <= end_col; i++) { + // The column separation may shrink with the expand option. + if (!(flags & EXPAND)) + printfs("+%1n", as_string(column_separation[i - 1])); + printfs("+\\n[%1]", span_width_reg(i, i)); + } + prints(")\n"); + printfs(".nr " NEEDED_REG " \\n[" NEEDED_REG "]/%1\n", + as_string(end_col - start_col + 1)); + prints(".if \\n[" NEEDED_REG "] \\{"); + for (i = start_col; i <= end_col; i++) + printfs(".nr %1 +\\n[" NEEDED_REG "]\n", + span_width_reg(i, i)); + int equal_flag = 0; + for (i = start_col; i <= end_col && !equal_flag; i++) + if (equal[i]) + equal_flag = 1; + if (equal_flag) { + for (i = 0; i < ncolumns; i++) + if (i < start_col || i > end_col) + printfs(".nr %1 +\\n[" NEEDED_REG "]\n", + span_width_reg(i, i)); + } + prints(".\\}\n"); +} + + +void table::sum_columns(int start_col, int end_col) +{ + assert(end_col > start_col); + printfs(".nr %1 \\n[%2]", + span_width_reg(start_col, end_col), + span_width_reg(start_col, start_col)); + for (int i = start_col + 1; i <= end_col; i++) + printfs("+(%1*\\n[" SEPARATION_FACTOR_REG "])+\\n[%2]", + as_string(column_separation[i - 1]), + span_width_reg(i, i)); + prints('\n'); +} + +horizontal_span::horizontal_span(int sc, int ec, horizontal_span *p) +: start_col(sc), end_col(ec), next(p) +{ +} + +void table::build_span_list() +{ + span_list = 0; + table_entry *p = entry_list; + while (p) { + if (p->end_col != p->start_col) { + horizontal_span *q; + for (q = span_list; q; q = q->next) + if (q->start_col == p->start_col + && q->end_col == p->end_col) + break; + if (!q) + span_list = new horizontal_span(p->start_col, p->end_col, span_list); + } + p = p->next; + } + // Now sort span_list primarily by order of end_row, and secondarily + // by reverse order of start_row. This ensures that if we divide + // spans using the order in span_list, we will get reasonable results. + horizontal_span *unsorted = span_list; + span_list = 0; + while (unsorted) { + horizontal_span **pp; + for (pp = &span_list; *pp; pp = &(*pp)->next) + if (unsorted->end_col < (*pp)->end_col + || (unsorted->end_col == (*pp)->end_col + && (unsorted->start_col > (*pp)->start_col))) + break; + horizontal_span *tem = unsorted->next; + unsorted->next = *pp; + *pp = unsorted; + unsorted = tem; + } +} + + +void table::compute_separation_factor() +{ + if (flags & (ALLBOX|BOX|DOUBLEBOX)) + left_separation = right_separation = 1; + else { + for (int i = 0; i < nrows; i++) { + if (vline[i][0] > 0) + left_separation = 1; + if (vline[i][ncolumns] > 0) + right_separation = 1; + } + } + if (flags & EXPAND) { + int total_sep = left_separation + right_separation; + int i; + for (i = 0; i < ncolumns - 1; i++) + total_sep += column_separation[i]; + if (total_sep != 0) { + // Don't let the separation factor be negative. + prints(".nr " SEPARATION_FACTOR_REG " \\n[.l]-\\n[.i]"); + for (i = 0; i < ncolumns; i++) + printfs("-\\n[%1]", span_width_reg(i, i)); + printfs("/%1>?0\n", as_string(total_sep)); + } + } +} + +void table::compute_column_positions() +{ + printfs(".nr %1 0\n", column_divide_reg(0)); + printfs(".nr %1 %2*\\n[" SEPARATION_FACTOR_REG "]\n", + column_start_reg(0), + as_string(left_separation)); + int i; + for (i = 1;; i++) { + printfs(".nr %1 \\n[%2]+\\n[%3]\n", + column_end_reg(i-1), + column_start_reg(i-1), + span_width_reg(i-1, i-1)); + if (i >= ncolumns) + break; + printfs(".nr %1 \\n[%2]+(%3*\\n[" SEPARATION_FACTOR_REG "])\n", + column_start_reg(i), + column_end_reg(i-1), + as_string(column_separation[i-1])); + printfs(".nr %1 \\n[%2]+\\n[%3]/2\n", + column_divide_reg(i), + column_end_reg(i-1), + column_start_reg(i)); + } + printfs(".nr %1 \\n[%2]+(%3*\\n[" SEPARATION_FACTOR_REG "])\n", + column_divide_reg(ncolumns), + column_end_reg(i-1), + as_string(right_separation)); + printfs(".nr TW \\n[%1]\n", + column_divide_reg(ncolumns)); + if (flags & DOUBLEBOX) { + printfs(".nr %1 +" DOUBLE_LINE_SEP "\n", column_divide_reg(0)); + printfs(".nr %1 -" DOUBLE_LINE_SEP "\n", column_divide_reg(ncolumns)); + } +} + +void table::make_columns_equal() +{ + int first = -1; // index of first equal column + int i; + for (i = 0; i < ncolumns; i++) + if (equal[i]) { + if (first < 0) { + printfs(".nr %1 \\n[%1]", span_width_reg(i, i)); + first = i; + } + else + printfs(">?\\n[%1]", span_width_reg(i, i)); + } + if (first >= 0) { + prints('\n'); + for (i = first + 1; i < ncolumns; i++) + if (equal[i]) + printfs(".nr %1 \\n[%2]\n", + span_width_reg(i, i), + span_width_reg(first, first)); + } +} + +void table::compute_widths() +{ + build_span_list(); + int i; + horizontal_span *p; + prints(".nr " SEPARATION_FACTOR_REG " 1n\n"); + for (i = 0; i < ncolumns; i++) { + init_span_reg(i, i); + if (!minimum_width[i].empty()) + printfs(".nr %1 %2\n", span_width_reg(i, i), minimum_width[i]); + } + for (p = span_list; p; p = p->next) + init_span_reg(p->start_col, p->end_col); + table_entry *q; + for (q = entry_list; q; q = q->next) + if (!q->mod->zero_width) + q->do_width(); + for (i = 0; i < ncolumns; i++) + compute_span_width(i, i); + for (p = span_list; p; p = p->next) + compute_span_width(p->start_col, p->end_col); + make_columns_equal(); + // Note that divide_span keeps equal width columns equal. + for (p = span_list; p; p = p->next) + divide_span(p->start_col, p->end_col); + for (p = span_list; p; p = p->next) + sum_columns(p->start_col, p->end_col); + int had_spanning_block = 0; + int had_equal_block = 0; + for (q = entry_list; q; q = q->next) + if (q->divert(ncolumns, minimum_width, + (flags & EXPAND) ? column_separation : 0)) { + if (q->end_col > q->start_col) + had_spanning_block = 1; + for (i = q->start_col; i <= q->end_col && !had_equal_block; i++) + if (equal[i]) + had_equal_block = 1; + } + if (had_equal_block) + make_columns_equal(); + if (had_spanning_block) + for (p = span_list; p; p = p->next) + divide_span(p->start_col, p->end_col); + compute_separation_factor(); + for (p = span_list; p; p = p->next) + sum_columns(p->start_col, p->end_col); + compute_column_positions(); +} + +void table::print_single_hline(int r) +{ + prints(".vs " LINE_SEP ">?\\n[.V]u\n" + ".ls 1\n" + "\\v'" BODY_DEPTH "'" + "\\s[\\n[" LINESIZE_REG "]]"); + if (r > nrows - 1) + prints("\\D'l |\\n[TW]u 0'"); + else { + int start_col = 0; + for (;;) { + while (start_col < ncolumns + && entry[r][start_col] != 0 + && entry[r][start_col]->start_row != r) + start_col++; + int end_col; + for (end_col = start_col; + end_col < ncolumns + && (entry[r][end_col] == 0 + || entry[r][end_col]->start_row == r); + end_col++) + ; + if (end_col <= start_col) + break; + printfs("\\h'|\\n[%1]u", + column_divide_reg(start_col)); + if ((r > 0 && vline[r-1][start_col] == 2) + || (r < nrows && vline[r][start_col] == 2)) + prints("-" HALF_DOUBLE_LINE_SEP); + prints("'"); + printfs("\\D'l |\\n[%1]u", + column_divide_reg(end_col)); + if ((r > 0 && vline[r-1][end_col] == 2) + || (r < nrows && vline[r][end_col] == 2)) + prints("+" HALF_DOUBLE_LINE_SEP); + prints(" 0'"); + start_col = end_col; + } + } + prints("\\s0\n"); + prints(".ls\n" + ".vs\n"); +} + +void table::print_double_hline(int r) +{ + prints(".vs " LINE_SEP "+" DOUBLE_LINE_SEP + ">?\\n[.V]u\n" + ".ls 1\n" + "\\v'" BODY_DEPTH "'" + "\\s[\\n[" LINESIZE_REG "]]"); + if (r > nrows - 1) + prints("\\v'-" DOUBLE_LINE_SEP "'" + "\\D'l |\\n[TW]u 0'" + "\\v'" DOUBLE_LINE_SEP "'" + "\\h'|0'" + "\\D'l |\\n[TW]u 0'"); + else { + int start_col = 0; + for (;;) { + while (start_col < ncolumns + && entry[r][start_col] != 0 + && entry[r][start_col]->start_row != r) + start_col++; + int end_col; + for (end_col = start_col; + end_col < ncolumns + && (entry[r][end_col] == 0 + || entry[r][end_col]->start_row == r); + end_col++) + ; + if (end_col <= start_col) + break; + const char *left_adjust = 0; + if ((r > 0 && vline[r-1][start_col] == 2) + || (r < nrows && vline[r][start_col] == 2)) + left_adjust = "-" HALF_DOUBLE_LINE_SEP; + const char *right_adjust = 0; + if ((r > 0 && vline[r-1][end_col] == 2) + || (r < nrows && vline[r][end_col] == 2)) + right_adjust = "+" HALF_DOUBLE_LINE_SEP; + printfs("\\v'-" DOUBLE_LINE_SEP "'" + "\\h'|\\n[%1]u", + column_divide_reg(start_col)); + if (left_adjust) + prints(left_adjust); + prints("'"); + printfs("\\D'l |\\n[%1]u", + column_divide_reg(end_col)); + if (right_adjust) + prints(right_adjust); + prints(" 0'"); + printfs("\\v'" DOUBLE_LINE_SEP "'" + "\\h'|\\n[%1]u", + column_divide_reg(start_col)); + if (left_adjust) + prints(left_adjust); + prints("'"); + printfs("\\D'l |\\n[%1]u", + column_divide_reg(end_col)); + if (right_adjust) + prints(right_adjust); + prints(" 0'"); + start_col = end_col; + } + } + prints("\\s0\n" + ".ls\n" + ".vs\n"); +} + +void table::compute_vrule_top_adjust(int start_row, int col, string &result) +{ + if (row_is_all_lines[start_row] && start_row < nrows - 1) { + if (row_is_all_lines[start_row] == 2) + result = LINE_SEP ">?\\n[.V]u" "+" DOUBLE_LINE_SEP; + else + result = LINE_SEP ">?\\n[.V]u"; + start_row++; + } + else { + result = ""; + if (start_row == 0) + return; + for (stuff *p = stuff_list; p && p->row <= start_row; p = p->next) + if (p->row == start_row + && (p->is_single_line() || p->is_double_line())) + return; + } + int left = 0; + if (col > 0) { + table_entry *e = entry[start_row-1][col-1]; + if (e && e->start_row == e->end_row) { + if (e->to_double_line_entry() != 0) + left = 2; + else if (e->to_single_line_entry() != 0) + left = 1; + } + } + int right = 0; + if (col < ncolumns) { + table_entry *e = entry[start_row-1][col]; + if (e && e->start_row == e->end_row) { + if (e->to_double_line_entry() != 0) + right = 2; + else if (e->to_single_line_entry() != 0) + right = 1; + } + } + if (row_is_all_lines[start_row-1] == 0) { + if (left > 0 || right > 0) { + result += "-" BODY_DEPTH "-" BAR_HEIGHT; + if ((left == 2 && right != 2) || (right == 2 && left != 2)) + result += "-" HALF_DOUBLE_LINE_SEP; + else if (left == 2 && right == 2) + result += "+" HALF_DOUBLE_LINE_SEP; + } + } + else if (row_is_all_lines[start_row-1] == 2) { + if ((left == 2 && right != 2) || (right == 2 && left != 2)) + result += "-" DOUBLE_LINE_SEP; + else if (left == 1 || right == 1) + result += "-" HALF_DOUBLE_LINE_SEP; + } +} + +void table::compute_vrule_bot_adjust(int end_row, int col, string &result) +{ + if (row_is_all_lines[end_row] && end_row > 0) { + end_row--; + result = ""; + } + else { + stuff *p; + for (p = stuff_list; p && p->row < end_row + 1; p = p->next) + ; + if (p && p->row == end_row + 1 && p->is_double_line()) { + result = "-" DOUBLE_LINE_SEP; + return; + } + if ((p != 0 && p->row == end_row + 1) + || end_row == nrows - 1) { + result = ""; + return; + } + if (row_is_all_lines[end_row+1] == 1) + result = LINE_SEP; + else if (row_is_all_lines[end_row+1] == 2) + result = LINE_SEP "+" DOUBLE_LINE_SEP; + else + result = ""; + } + int left = 0; + if (col > 0) { + table_entry *e = entry[end_row+1][col-1]; + if (e && e->start_row == e->end_row) { + if (e->to_double_line_entry() != 0) + left = 2; + else if (e->to_single_line_entry() != 0) + left = 1; + } + } + int right = 0; + if (col < ncolumns) { + table_entry *e = entry[end_row+1][col]; + if (e && e->start_row == e->end_row) { + if (e->to_double_line_entry() != 0) + right = 2; + else if (e->to_single_line_entry() != 0) + right = 1; + } + } + if (row_is_all_lines[end_row+1] == 0) { + if (left > 0 || right > 0) { + result = "1v-" BODY_DEPTH "-" BAR_HEIGHT; + if ((left == 2 && right != 2) || (right == 2 && left != 2)) + result += "+" HALF_DOUBLE_LINE_SEP; + else if (left == 2 && right == 2) + result += "-" HALF_DOUBLE_LINE_SEP; + } + } + else if (row_is_all_lines[end_row+1] == 2) { + if (left == 2 && right == 2) + result += "-" DOUBLE_LINE_SEP; + else if (left != 2 && right != 2 && (left == 1 || right == 1)) + result += "-" HALF_DOUBLE_LINE_SEP; + } +} + +void table::add_vertical_rule(int start_row, int end_row, int col, int is_double) +{ + vrule_list = new vertical_rule(start_row, end_row, col, is_double, + vrule_list); + compute_vrule_top_adjust(start_row, col, vrule_list->top_adjust); + compute_vrule_bot_adjust(end_row, col, vrule_list->bot_adjust); +} + +void table::build_vrule_list() +{ + int col; + if (flags & ALLBOX) { + for (col = 1; col < ncolumns; col++) { + int start_row = 0; + for (;;) { + while (start_row < nrows && vline_spanned(start_row, col)) + start_row++; + if (start_row >= nrows) + break; + int end_row = start_row; + while (end_row < nrows && !vline_spanned(end_row, col)) + end_row++; + end_row--; + add_vertical_rule(start_row, end_row, col, 0); + start_row = end_row + 1; + } + } + } + if (flags & (BOX|ALLBOX|DOUBLEBOX)) { + add_vertical_rule(0, nrows - 1, 0, 0); + add_vertical_rule(0, nrows - 1, ncolumns, 0); + } + for (int end_row = 0; end_row < nrows; end_row++) + for (col = 0; col < ncolumns+1; col++) + if (vline[end_row][col] > 0 + && !vline_spanned(end_row, col) + && (end_row == nrows - 1 + || vline[end_row+1][col] != vline[end_row][col] + || vline_spanned(end_row+1, col))) { + int start_row; + for (start_row = end_row - 1; + start_row >= 0 + && vline[start_row][col] == vline[end_row][col] + && !vline_spanned(start_row, col); + start_row--) + ; + start_row++; + add_vertical_rule(start_row, end_row, col, vline[end_row][col] > 1); + } + for (vertical_rule *p = vrule_list; p; p = p->next) + if (p->is_double) + for (int r = p->start_row; r <= p->end_row; r++) { + if (p->col > 0 && entry[r][p->col-1] != 0 + && entry[r][p->col-1]->end_col == p->col-1) { + int is_corner = r == p->start_row || r == p->end_row; + entry[r][p->col-1]->note_double_vrule_on_right(is_corner); + } + if (p->col < ncolumns && entry[r][p->col] != 0 + && entry[r][p->col]->start_col == p->col) { + int is_corner = r == p->start_row || r == p->end_row; + entry[r][p->col]->note_double_vrule_on_left(is_corner); + } + } +} + +void table::define_bottom_macro() +{ + prints(".eo\n" + ".de T#\n" + ".if !\\n[" SUPPRESS_BOTTOM_REG "] \\{" + "." REPEATED_VPT_MACRO " 0\n" + ".mk " SAVED_VERTICAL_POS_REG "\n"); + if (flags & (BOX|ALLBOX|DOUBLEBOX)) { + prints(".if \\n[T.]&\\n[" NEED_BOTTOM_RULE_REG "] \\{"); + print_single_hline(0); + prints(".\\}\n"); + } + prints(".ls 1\n"); + for (vertical_rule *p = vrule_list; p; p = p->next) + p->contribute_to_bottom_macro(this); + if (flags & DOUBLEBOX) + prints(".if \\n[T.] \\{.vs " DOUBLE_LINE_SEP ">?\\n[.V]u\n" + "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]" + "\\D'l \\n[TW]u 0'\\s0\n" + ".vs\n" + ".\\}\n" + ".if \\n[" LAST_PASSED_ROW_REG "]>=0 " + ".nr " TOP_REG " \\n[#T]-" DOUBLE_LINE_SEP "\n" + ".sp -1\n" + "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]" + "\\D'l 0 |\\n[" TOP_REG "]u-1v'\\s0\n" + ".sp -1\n" + "\\v'" BODY_DEPTH "'\\h'|\\n[TW]u'\\s[\\n[" LINESIZE_REG "]]" + "\\D'l 0 |\\n[" TOP_REG "]u-1v'\\s0\n"); + prints(".ls\n"); + prints(".nr " LAST_PASSED_ROW_REG " \\n[" CURRENT_ROW_REG "]\n" + ".sp |\\n[" SAVED_VERTICAL_POS_REG "]u\n" + "." REPEATED_VPT_MACRO " 1\n" + ".\\}\n" + "..\n" + ".ec\n"); +} + + +// is the vertical line before column c in row r horizontally spanned? + +int table::vline_spanned(int r, int c) +{ + assert(r >= 0 && r < nrows && c >= 0 && c < ncolumns + 1); + return (c != 0 && c != ncolumns && entry[r][c] != 0 + && entry[r][c]->start_col != c + // horizontally spanning lines don't count + && entry[r][c]->to_double_line_entry() == 0 + && entry[r][c]->to_single_line_entry() == 0); +} + +int table::row_begins_section(int r) +{ + assert(r >= 0 && r < nrows); + for (int i = 0; i < ncolumns; i++) + if (entry[r][i] && entry[r][i]->start_row != r) + return 0; + return 1; +} + +int table::row_ends_section(int r) +{ + assert(r >= 0 && r < nrows); + for (int i = 0; i < ncolumns; i++) + if (entry[r][i] && entry[r][i]->end_row != r) + return 0; + return 1; +} + +void table::do_row(int r) +{ + if (!(flags & NOKEEP) && row_begins_section(r)) + prints("." KEEP_MACRO_NAME "\n"); + int had_line = 0; + stuff *p; + for (p = stuff_list; p && p->row < r; p = p->next) + ; + for (stuff *p1 = p; p1 && p1->row == r; p1 = p1->next) + if (!p1->printed && (p1->is_single_line() || p1->is_double_line())) { + had_line = 1; + break; + } + if (!had_line && !row_is_all_lines[r]) + printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r)); + had_line = 0; + for (; p && p->row == r; p = p->next) + if (!p->printed) { + p->print(this); + if (!had_line && (p->is_single_line() || p->is_double_line())) { + printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r)); + had_line = 1; + } + } + // Change the row *after* printing the stuff list (which might contain .TH). + printfs("\\*[" TRANSPARENT_STRING_NAME "].nr " CURRENT_ROW_REG " %1\n", + as_string(r)); + if (!had_line && row_is_all_lines[r]) + printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r)); + // we might have had a .TH, for example, since we last tried + if (!(flags & NOKEEP) && row_begins_section(r)) + prints("." KEEP_MACRO_NAME "\n"); + printfs(".mk %1\n", row_start_reg(r)); + prints(".mk " BOTTOM_REG "\n" + "." REPEATED_VPT_MACRO " 0\n"); + int c; + int row_is_blank = 1; + int first_start_row = r; + for (c = 0; c < ncolumns; c++) { + table_entry *e = entry[r][c]; + if (e) { + if (e->end_row == r) { + e->do_depth(); + if (e->start_row < first_start_row) + first_start_row = e->start_row; + row_is_blank = 0; + } + c = e->end_col; + } + } + if (row_is_blank) + prints(".nr " BOTTOM_REG " +1v\n"); + if (row_is_all_lines[r]) { + prints(".vs " LINE_SEP); + if (row_is_all_lines[r] == 2) + prints("+" DOUBLE_LINE_SEP); + prints(">?\\n[.V]u\n.ls 1\n"); + prints("\\&"); + prints("\\v'" BODY_DEPTH); + if (row_is_all_lines[r] == 2) + prints("-" HALF_DOUBLE_LINE_SEP); + prints("'"); + for (c = 0; c < ncolumns; c++) { + table_entry *e = entry[r][c]; + if (e) { + if (e->end_row == e->start_row) + e->to_simple_entry()->simple_print(1); + c = e->end_col; + } + } + prints("\n"); + prints(".ls\n" + ".vs\n"); + prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n"); + printfs(".sp |\\n[%1]u\n", row_start_reg(r)); + } + for (int i = row_is_all_lines[r] ? r - 1 : r; + i >= first_start_row; + i--) { + simple_entry *first = 0; + for (c = 0; c < ncolumns; c++) { + table_entry *e = entry[r][c]; + if (e) { + if (e->end_row == r && e->start_row == i) { + simple_entry *simple = e->to_simple_entry(); + if (simple) { + if (!first) { + prints(".ta"); + first = simple; + } + simple->add_tab(); + } + } + c = e->end_col; + } + } + if (first) { + prints('\n'); + first->position_vertically(); + first->set_location(); + prints("\\&"); + first->simple_print(0); + for (c = first->end_col + 1; c < ncolumns; c++) { + table_entry *e = entry[r][c]; + if (e) { + if (e->end_row == r && e->start_row == i) { + simple_entry *simple = e->to_simple_entry(); + if (simple) + simple->simple_print(0); + } + c = e->end_col; + } + } + prints('\n'); + prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n"); + printfs(".sp |\\n[%1]u\n", row_start_reg(r)); + } + } + for (c = 0; c < ncolumns; c++) { + table_entry *e = entry[r][c]; + if (e) { + if (e->end_row == r && e->to_simple_entry() == 0) { + e->position_vertically(); + e->print(); + prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n"); + printfs(".sp |\\n[%1]u\n", row_start_reg(r)); + } + c = e->end_col; + } + } + prints("." REPEATED_VPT_MACRO " 1\n" + ".sp |\\n[" BOTTOM_REG "]u\n" + "\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG " 1\n"); + if (r != nrows - 1 && (flags & ALLBOX)) { + print_single_hline(r + 1); + prints("\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG " 0\n"); + } + if (r != nrows - 1) { + if (p && p->row == r + 1 + && (p->is_single_line() || p->is_double_line())) { + p->print(this); + prints("\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG + " 0\n"); + } + int printed_one = 0; + for (vertical_rule *p = vrule_list; p; p = p->next) + if (p->end_row == r) { + if (!printed_one) { + prints("." REPEATED_VPT_MACRO " 0\n"); + printed_one = 1; + } + p->print(); + } + if (printed_one) + prints("." REPEATED_VPT_MACRO " 1\n"); + if (!(flags & NOKEEP) && row_ends_section(r)) + prints("." RELEASE_MACRO_NAME "\n"); + } +} + +void table::do_top() +{ + prints(".fc \002\003\n"); + if (!(flags & NOKEEP) && (flags & (BOX|DOUBLEBOX|ALLBOX))) + prints("." TABLE_KEEP_MACRO_NAME "\n"); + if (flags & DOUBLEBOX) { + prints(".ls 1\n" + ".vs " LINE_SEP ">?\\n[.V]u\n" + "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]\\D'l \\n[TW]u 0'\\s0\n" + ".vs\n" + "." REPEATED_MARK_MACRO " " TOP_REG "\n" + ".vs " DOUBLE_LINE_SEP ">?\\n[.V]u\n"); + printfs("\\v'" BODY_DEPTH "'" + "\\s[\\n[" LINESIZE_REG "]]" + "\\h'\\n[%1]u'" + "\\D'l |\\n[%2]u 0'" + "\\s0" + "\n", + column_divide_reg(0), + column_divide_reg(ncolumns)); + prints(".ls\n" + ".vs\n"); + } + else if (flags & (ALLBOX|BOX)) { + print_single_hline(0); + } + //printfs(".mk %1\n", row_top_reg(0)); +} + +void table::do_bottom() +{ + // print stuff after last row + for (stuff *p = stuff_list; p; p = p->next) + if (p->row > nrows - 1) + p->print(this); + if (!(flags & NOKEEP)) + prints("." RELEASE_MACRO_NAME "\n"); + printfs(".mk %1\n", row_top_reg(nrows)); + prints(".nr " NEED_BOTTOM_RULE_REG " 1\n" + ".nr T. 1\n" + ".T#\n"); + if (!(flags & NOKEEP) && (flags & (BOX|DOUBLEBOX|ALLBOX))) + prints("." TABLE_RELEASE_MACRO_NAME "\n"); + if (flags & DOUBLEBOX) + prints(".sp " DOUBLE_LINE_SEP "\n"); + prints("." RESET_MACRO_NAME "\n" + ".fc\n" + ".cp \\n(" COMPATIBLE_REG "\n"); +} + +int table::get_nrows() +{ + return nrows; +} + +const char *last_filename = 0; + +void set_troff_location(const char *fn, int ln) +{ + if (!location_force_filename && last_filename != 0 + && strcmp(fn, last_filename) == 0) + printfs(".lf %1\n", as_string(ln)); + else { + printfs(".lf %1 %2\n", as_string(ln), fn); + last_filename = fn; + location_force_filename = 0; + } +} + +void printfs(const char *s, const string &arg1, const string &arg2, + const string &arg3, const string &arg4, const string &arg5) +{ + if (s) { + char c; + while ((c = *s++) != '\0') { + if (c == '%') { + switch (*s++) { + case '1': + prints(arg1); + break; + case '2': + prints(arg2); + break; + case '3': + prints(arg3); + break; + case '4': + prints(arg4); + break; + case '5': + prints(arg5); + break; + case '6': + case '7': + case '8': + case '9': + break; + case '%': + prints('%'); + break; + default: + assert(0); + } + } + else + prints(c); + } + } +} + |