summaryrefslogtreecommitdiffstats
path: root/src/preproc/tbl/table.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/preproc/tbl/table.cpp')
-rw-r--r--src/preproc/tbl/table.cpp3161
1 files changed, 3161 insertions, 0 deletions
diff --git a/src/preproc/tbl/table.cpp b/src/preproc/tbl/table.cpp
new file mode 100644
index 0000000..c391c90
--- /dev/null
+++ b/src/preproc/tbl/table.cpp
@@ -0,0 +1,3161 @@
+/* Copyright (C) 1989-2023 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#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 SEPARATION_FACTOR_REG PREFIX "sep"
+#define LEFTOVER_FACTOR_REG PREFIX "leftover"
+#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 USE_KEEPS_REG PREFIX "usekeeps"
+#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 SAVED_TABS_NAME PREFIX "tabs"
+#define SAVED_INTER_WORD_SPACE_SIZE PREFIX "ss"
+#define SAVED_INTER_SENTENCE_SPACE_SIZE PREFIX "sss"
+#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"
+#define SAVED_HYPHENATION_MODE_REG PREFIX "hyphmode"
+#define SAVED_HYPHENATION_LANG_NAME PREFIX "hyphlang"
+#define SAVED_HYPHENATION_MAX_LINES_REG PREFIX "hyphmaxlines"
+#define SAVED_HYPHENATION_MARGIN_REG PREFIX "hyphmargin"
+#define SAVED_HYPHENATION_SPACE_REG PREFIX "hyphspace"
+#define SAVED_NUMBERING_LINENO PREFIX "linenumber"
+#define SAVED_NUMBERING_SUPPRESSION_COUNT PREFIX "linenumbersuppresscnt"
+#define STARTING_PAGE_REG PREFIX "starting-page"
+#define IS_BOXED_REG PREFIX "is-boxed"
+#define PREVIOUS_PAGE_REG PREFIX "previous-page"
+
+// this must be one character
+#define COMPATIBLE_REG PREFIX "c"
+
+// for use with `ig` requests embedded inside macro definitions
+#define NOP_NAME PREFIX "nop"
+
+#define AVAILABLE_WIDTH_REG PREFIX "available-width"
+#define EXPAND_REG PREFIX "expansion-amount"
+
+#define LEADER_REG PREFIX LEADER
+
+#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, int);
+string block_diversion_name(int, int);
+string block_height_reg(int, int);
+string span_width_reg(int, int);
+string span_left_numeric_width_reg(int, int);
+string span_right_numeric_width_reg(int, int);
+string span_alphabetic_width_reg(int, int);
+string column_separation_reg(int);
+string row_start_reg(int);
+string column_start_reg(int);
+string column_end_reg(int);
+string column_divide_reg(int);
+string row_top_reg(int);
+
+void set_inline_modifier(const entry_modifier *);
+void restore_inline_modifier(const entry_modifier *);
+void set_modifier(const entry_modifier *);
+int find_decimal_point(const char *, char, const char *);
+
+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;
+ int start_col;
+ int end_col;
+ horizontal_span(int, int, horizontal_span *);
+};
+
+class single_line_entry;
+class double_line_entry;
+class 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;
+ int start_col;
+ int end_col;
+ const table *parent;
+ const entry_modifier *mod;
+public:
+ void set_location();
+ table_entry(const table *, const entry_modifier *);
+ virtual ~table_entry();
+ virtual int divert(int, const string *, int *, int);
+ 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 table *, 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 table *, const entry_modifier *);
+ int line_type();
+};
+
+class text_entry : public simple_entry {
+protected:
+ char *contents;
+ void print_contents();
+public:
+ text_entry(const table *, const entry_modifier *, char *);
+ ~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(const table *, const entry_modifier *, char *);
+ void simple_print(int);
+};
+
+class simple_text_entry : public text_entry {
+public:
+ simple_text_entry(const table *, const entry_modifier *, char *);
+ void do_width();
+};
+
+class left_text_entry : public simple_text_entry {
+public:
+ left_text_entry(const table *, const entry_modifier *, char *);
+ void simple_print(int);
+ void add_tab();
+};
+
+class right_text_entry : public simple_text_entry {
+public:
+ right_text_entry(const table *, const entry_modifier *, char *);
+ void simple_print(int);
+ void add_tab();
+};
+
+class center_text_entry : public simple_text_entry {
+public:
+ center_text_entry(const table *, const entry_modifier *, char *);
+ void simple_print(int);
+ void add_tab();
+};
+
+class numeric_text_entry : public text_entry {
+ int dot_pos;
+public:
+ numeric_text_entry(const table *, const entry_modifier *, char *, int);
+ void do_width();
+ void simple_print(int);
+};
+
+class alphabetic_text_entry : public text_entry {
+public:
+ alphabetic_text_entry(const table *, const entry_modifier *, char *);
+ 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 table *, 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 table *, const entry_modifier *);
+ 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 table *, const entry_modifier *);
+ 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 table *, const entry_modifier *);
+ void simple_print(int);
+ int line_type();
+};
+
+class short_double_line_entry : public simple_entry {
+public:
+ short_double_line_entry(const table *, const entry_modifier *);
+ void simple_print(int);
+ int line_type();
+};
+
+class block_entry : public table_entry {
+ char *contents;
+protected:
+ void do_divert(int, int, const string *, int *, int);
+public:
+ block_entry(const table *, const entry_modifier *, char *);
+ ~block_entry();
+ int divert(int, const string *, int *, int);
+ void do_depth();
+ void position_vertically();
+ void print() = 0;
+};
+
+class left_block_entry : public block_entry {
+public:
+ left_block_entry(const table *, const entry_modifier *, char *);
+ void print();
+};
+
+class right_block_entry : public block_entry {
+public:
+ right_block_entry(const table *, const entry_modifier *, char *);
+ void print();
+};
+
+class center_block_entry : public block_entry {
+public:
+ center_block_entry(const table *, const entry_modifier *, char *);
+ void print();
+};
+
+class alphabetic_block_entry : public block_entry {
+public:
+ alphabetic_block_entry(const table *, const entry_modifier *, char *);
+ void print();
+ int divert(int, const string *, int *, int);
+};
+
+table_entry::table_entry(const table *p, const entry_modifier *m)
+: next(0), input_lineno(-1), input_filename(0),
+ start_row(-1), end_row(-1), start_col(-1), end_col(-1), parent(p), mod(m)
+{
+}
+
+table_entry::~table_entry()
+{
+}
+
+int table_entry::divert(int, const string *, int *, 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 table *p, const entry_modifier *m)
+: table_entry(p, 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:
+ // Perform 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 == "simple entry vertical position modifier not TOP,"
+ " CENTER, or BOTTOM");
+ }
+}
+
+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 table *p, const entry_modifier *m)
+: simple_entry(p, m)
+{
+}
+
+int empty_entry::line_type()
+{
+ return 0;
+}
+
+text_entry::text_entry(const table *p, const entry_modifier *m, char *s)
+: simple_entry(p, m), contents(s)
+{
+}
+
+text_entry::~text_entry()
+{
+ free(contents);
+}
+
+repeated_char_entry::repeated_char_entry(const table *p,
+ const entry_modifier *m, char *s)
+: text_entry(p, m, s)
+{
+}
+
+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(const table *p,
+ const entry_modifier *m, char *s)
+: text_entry(p, m, s)
+{
+}
+
+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(const table *p,
+ const entry_modifier *m, char *s)
+: simple_text_entry(p, m, s)
+{
+}
+
+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(const table *p,
+ const entry_modifier *m, char *s)
+: simple_text_entry(p, m, s)
+{
+}
+
+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(const table *p,
+ const entry_modifier *m, char *s)
+: simple_text_entry(p, m, s)
+{
+}
+
+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(const table *p,
+ const entry_modifier *m,
+ char *s, int pos)
+: text_entry(p, m, s), 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(const table *p,
+ const entry_modifier *m,
+ char *s)
+: text_entry(p, m, s)
+{
+}
+
+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(const table *p, const entry_modifier *m, char *s)
+: table_entry(p, m), contents(s)
+{
+}
+
+block_entry::~block_entry()
+{
+ 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:
+ // Perform 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 == "block entry vertical position modifier not TOP,"
+ " CENTER, or BOTTOM");
+ }
+ if (mod->stagger)
+ prints(".sp -.5v\n");
+}
+
+int block_entry::divert(int ncols, const string *mw, int *sep, int do_expand)
+{
+ do_divert(0, ncols, mw, sep, do_expand);
+ return 1;
+}
+
+void block_entry::do_divert(int alphabetic, int ncols, const string *mw,
+ int *sep, int do_expand)
+{
+ int i;
+ for (i = start_col; i <= end_col; i++)
+ if (parent->expand[i])
+ break;
+ if (i > end_col) {
+ if (do_expand)
+ return;
+ }
+ else {
+ if (!do_expand)
+ return;
+ }
+ printfs(".di %1\n", block_diversion_name(start_row, start_col));
+ prints(".if \\n[" SAVED_FILL_REG "] .fi\n"
+ ".in 0\n");
+ prints(".ll ");
+ for (i = start_col; i <= end_col; i++)
+ if (mw[i].empty() && !parent->expand[i])
+ break;
+ if (i > end_col) {
+ // Every column spanned by this entry has a minimum width.
+ for (int j = start_col; j <= end_col; j++) {
+ if (j > start_col) {
+ if (sep)
+ printfs("+%1n", as_string(sep[j - 1]));
+ prints('+');
+ }
+ if (parent->expand[j])
+ prints("\\n[" EXPAND_REG "]u");
+ else
+ printfs("(n;%1)", mw[j]);
+ }
+ printfs(">?\\n[%1]u", span_width_reg(start_col, end_col));
+ }
+ else
+ // Assign each column with a block entry 1/(n+1) of the line
+ // width, where n is the column count.
+ 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");
+ prints(".ss \\n[" SAVED_INTER_WORD_SPACE_SIZE "]"
+ " \\n[" SAVED_INTER_SENTENCE_SPACE_SIZE "]\n");
+ prints(".cp \\n(" COMPATIBLE_REG "\n");
+ set_modifier(mod);
+ 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_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(const table *p,
+ const entry_modifier *m, char *s)
+: block_entry(p, m, s)
+{
+}
+
+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(const table *p,
+ const entry_modifier *m, char *s)
+: block_entry(p, m, s)
+{
+}
+
+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(const table *p,
+ const entry_modifier *m, char *s)
+: block_entry(p, m, s)
+{
+}
+
+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(const table *p,
+ const entry_modifier *m,
+ char *s)
+: block_entry(p, m, s)
+{
+}
+
+int alphabetic_block_entry::divert(int ncols, const string *mw, int *sep,
+ int do_expand)
+{
+ do_divert(1, ncols, mw, sep, do_expand);
+ 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 table *p, const entry_modifier *m)
+: simple_entry(p, 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 table *p, const entry_modifier *m)
+: line_entry(p, 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 table *p,
+ const entry_modifier *m)
+: line_entry(p, 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 table *p, const entry_modifier *m)
+: simple_entry(p, 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 table *p,
+ const entry_modifier *m)
+: simple_entry(p, 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));
+ }
+ if (!m->macro.empty())
+ printfs(".%1\n", m->macro);
+}
+
+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) : next(0), row(r), printed(0)
+{
+}
+
+stuff::~stuff()
+{
+}
+
+struct text_stuff : public stuff {
+ string contents;
+ const char *filename;
+ int lineno;
+
+ text_stuff(const string &, int, const char *, int);
+ ~text_stuff();
+ void print(table *);
+};
+
+text_stuff::text_stuff(const string &s, int r, const char *fn, int ln)
+: stuff(r), contents(s), 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);
+ 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);
+ 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;
+ int col;
+ char is_double;
+ string top_adjust;
+ string bot_adjust;
+
+ vertical_rule(int, int, int, int, 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)
+: next(p), start_row(sr), end_row(er), col(c), is_double(dbl)
+{
+}
+
+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(" \\{\\\n");
+ 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)
+: nrows(0), ncolumns(nc), linesize(ls), decimal_point_char(dpc),
+ vrule_list(0), stuff_list(0), span_list(0),
+ entry_list(0), entry_list_tailp(&entry_list), entry(0),
+ vline(0), row_is_all_lines(0), left_separation(0),
+ right_separation(0), total_separation(0), allocated_rows(0), flags(f)
+{
+ minimum_width = new string[ncolumns];
+ column_separation = ncolumns > 1 ? new int[ncolumns - 1] : 0;
+ equal = new char[ncolumns];
+ expand = new char[ncolumns];
+ int i;
+ for (i = 0; i < ncolumns; i++) {
+ equal[i] = 0;
+ expand[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++) {
+ delete[] entry[i];
+ delete[] vline[i];
+ }
+ delete[] entry;
+ delete[] vline;
+ while (entry_list) {
+ table_entry *tem = entry_list;
+ entry_list = entry_list->next;
+ delete tem;
+ }
+ delete[] minimum_width;
+ delete[] column_separation;
+ delete[] equal;
+ delete[] expand;
+ 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;
+ }
+ 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::set_expand_column(int c)
+{
+ assert(c >= 0 && c < ncolumns);
+ expand[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);
+ delete[] old_entry;
+ char **old_vline = vline;
+ vline = new char*[allocated_rows];
+ memcpy(vline, old_vline, sizeof(char*)*old_allocated_rows);
+ 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 (0 == r) {
+ error("first row cannot be vertically spanned");
+ return;
+ }
+ table_entry *e = entry[r][c];
+ if (e) {
+ assert(e->start_row <= r);
+ assert(r <= e->end_row);
+ assert(e->start_col <= c);
+ assert(c <= e->end_col);
+ assert((e->end_row - e->start_row) > 0);
+ assert((e->end_col - e->start_col) > 0);
+ return;
+ }
+ e = entry[r-1][c];
+ // e can be a null pointer if we had an empty entry or an error
+ if (0 == e)
+ 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;
+ char *s = str.extract();
+ if (str.search('\n') >= 0) {
+ bool was_changed = false;
+ for (int i = 0; s[i] != '\0'; i++)
+ if ((i > 0) && (s[(i - 1)] == '\\') && (s[i] == 'R')) {
+ s[i] = '&';
+ was_changed = true;
+ }
+ if (was_changed)
+ error_with_file_and_line(fn, ln, "repeating a glyph with '\\R'"
+ " is not allowed in a text block");
+ }
+ if (str == "\\_") {
+ e = new short_line_entry(this, f);
+ }
+ else if (str == "\\=") {
+ e = new short_double_line_entry(this, 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(this, 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(this, f);
+ }
+ else if (str == "\\^") {
+ if (r == 0) {
+ error("first row cannot contain a vertical span entry '\\^'");
+ e = new empty_entry(this, f);
+ }
+ else
+ do_vspan(r, c);
+ }
+ else {
+ int is_block = str.search('\n') >= 0;
+ switch (f->type) {
+ case FORMAT_SPAN:
+ assert(str.empty());
+ do_hspan(r, c);
+ break;
+ case FORMAT_LEFT:
+ if (!str.empty()) {
+ if (is_block)
+ e = new left_block_entry(this, f, s);
+ else
+ e = new left_text_entry(this, f, s);
+ }
+ else
+ e = new empty_entry(this, f);
+ break;
+ case FORMAT_CENTER:
+ if (!str.empty()) {
+ if (is_block)
+ e = new center_block_entry(this, f, s);
+ else
+ e = new center_text_entry(this, f, s);
+ }
+ else
+ e = new empty_entry(this, f);
+ break;
+ case FORMAT_RIGHT:
+ if (!str.empty()) {
+ if (is_block)
+ e = new right_block_entry(this, f, s);
+ else
+ e = new right_text_entry(this, f, s);
+ }
+ else
+ e = new empty_entry(this, f);
+ break;
+ case FORMAT_NUMERIC:
+ if (!str.empty()) {
+ if (is_block) {
+ error_with_file_and_line(fn, ln, "can't have numeric text block");
+ e = new left_block_entry(this, f, s);
+ }
+ else {
+ int pos = find_decimal_point(s, decimal_point_char, delim);
+ if (pos < 0)
+ e = new center_text_entry(this, f, s);
+ else
+ e = new numeric_text_entry(this, f, s, pos);
+ }
+ }
+ else
+ e = new empty_entry(this, f);
+ break;
+ case FORMAT_ALPHABETIC:
+ if (!str.empty()) {
+ if (is_block)
+ e = new alphabetic_block_entry(this, f, s);
+ else
+ e = new alphabetic_text_entry(this, f, s);
+ }
+ else
+ e = new empty_entry(this, f);
+ break;
+ case FORMAT_VSPAN:
+ do_vspan(r, c);
+ break;
+ case FORMAT_HLINE:
+ if ((str.length() != 0) && (str != "\\&"))
+ error_with_file_and_line(fn, ln,
+ "ignoring non-empty data entry using"
+ " '_' column classifier");
+ e = new single_line_entry(this, f);
+ break;
+ case FORMAT_DOUBLE_HLINE:
+ if ((str.length() != 0) && (str != "\\&"))
+ error_with_file_and_line(fn, ln,
+ "ignoring non-empty data entry using"
+ " '=' column classifier");
+ e = new double_line_entry(this, f);
+ break;
+ default:
+ assert(0 == "table column format not in FORMAT_{SPAN,LEFT,CENTER,"
+ "RIGHT,NUMERIC,ALPHABETIC,VSPAN,HLINE,DOUBLE_HLINE}");
+ }
+ }
+ 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);
+ bool lwarned = false;
+ bool twarned = false;
+ for (int i = 0; i < ncolumns+1; i++) {
+ assert(v[i] < 3);
+ if (v[i] && (flags & (BOX | ALLBOX | DOUBLEBOX)) && (i == 0)
+ && (!lwarned)) {
+ error("ignoring vertical line at leading edge of boxed table");
+ lwarned = true;
+ }
+ else if (v[i] && (flags & (BOX | ALLBOX | DOUBLEBOX))
+ && (i == ncolumns) && (!twarned)) {
+ error("ignoring vertical line at trailing edge of boxed table");
+ twarned = true;
+ }
+ else
+ 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 "] \\{\\\n");
+ prints(". in +(u;\\n[.l]-\\n[.i]-\\n[TW]/2>?-\\n[.i])\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++) {
+ bool had_single = false;
+ bool had_double = false;
+ bool had_non_line = false;
+ 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 = true;
+ break;
+ case 0:
+ // empty
+ break;
+ case 1:
+ had_single = true;
+ break;
+ case 2:
+ had_double = true;
+ break;
+ default:
+ assert(0 == "table entry line type not in {-1, 0, 1, 2}");
+ }
+ 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;
+ }
+}
+
+int table::count_expand_columns()
+{
+ int count = 0;
+ for (int i = 0; i < ncolumns; i++)
+ if (expand[i])
+ count++;
+ return count;
+}
+
+void table::init_output()
+{
+ prints(".\\\" initialize output\n");
+ 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");
+ if (compatible_flag)
+ prints(".ds " LEADER_REG " \\a\n");
+ if (!(flags & NOKEEP))
+ prints(".if !r " USE_KEEPS_REG " .nr " USE_KEEPS_REG " 1\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"
+ ". hy \\\\n[" SAVED_HYPHENATION_MODE_REG "]\n"
+ ". hla \\\\*[" SAVED_HYPHENATION_LANG_NAME "]\n"
+ ". hlm \\\\n[" SAVED_HYPHENATION_MAX_LINES_REG "]\n"
+ ". hym \\\\n[" SAVED_HYPHENATION_MARGIN_REG "]\n"
+ ". hys \\\\n[" SAVED_HYPHENATION_SPACE_REG "]\n"
+ ". ad \\n[.j]\n"
+ ". ie \\n[.u] .fi\n"
+ ". el .nf\n"
+ ". ce \\n[.ce]\n"
+ ". ta \\\\*[" SAVED_TABS_NAME "]\n"
+ ". ss \\\\n[" SAVED_INTER_WORD_SPACE_SIZE "]"
+ " \\\\n[" SAVED_INTER_SENTENCE_SPACE_SIZE "]\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"
+ ".ds " SAVED_TABS_NAME " \\n[.tabs]\n"
+ ".nr " SAVED_INTER_WORD_SPACE_SIZE " \\n[.ss]\n"
+ ".nr " SAVED_INTER_SENTENCE_SPACE_SIZE " \\n[.sss]\n"
+ ".nr " SAVED_HYPHENATION_MODE_REG " \\n[.hy]\n"
+ ".ds " SAVED_HYPHENATION_LANG_NAME " \\n[.hla]\n"
+ ".nr " SAVED_HYPHENATION_MAX_LINES_REG " (\\n[.hlm])\n"
+ ".nr " SAVED_HYPHENATION_MARGIN_REG " \\n[.hym]\n"
+ ".nr " SAVED_HYPHENATION_SPACE_REG " \\n[.hys]\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]'' \\{\\\n"
+ ". 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"
+ // Protect '#' in macro name from being interpreted by eqn.
+ ".ig\n"
+ ".EQ\n"
+ "delim off\n"
+ ".EN\n"
+ "..\n"
+ ".de " RELEASE_MACRO_NAME "\n"
+ ". if \\n[" SECTION_DIVERSION_FLAG_REG "] \\{\\\n"
+ ". 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] \\{\\\n"
+ ". 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 (!(flags & NOWARN)) {
+ prints(". if \\n[.t]<=\\n[" SAVED_DN_REG "] \\{\\\n");
+ // eqn(1) delimiters have already been switched off.
+ entry_list->set_location();
+ // Since we turn off traps, troff won't go into an infinite loop
+ // when we output the table row; it will just flow off the bottom
+ // of the page.
+ prints(". tmc \\n[.F]:\\n[.c]: warning:\n"
+ ". tm1 \" table row does not fit on page \\n%\n");
+ prints(". \\}\n");
+ }
+ prints(". nf\n"
+ ". ls 1\n"
+ ". " SECTION_DIVERSION_NAME "\n"
+ ". ls\n"
+ ". rm " SECTION_DIVERSION_NAME "\n"
+ ". \\}\n"
+ "..\n"
+ ".ig\n"
+ ".EQ\n"
+ "delim on\n"
+ ".EN\n"
+ "..\n"
+ ".nr " TABLE_DIVERSION_FLAG_REG " 0\n"
+ ".de " TABLE_KEEP_MACRO_NAME "\n"
+ ". if '\\n[.z]'' \\{\\\n"
+ ". 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 "] \\{\\\n"
+ ". 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 "] \\{\\\n");
+ // Protect characters in diagnostic message (especially :, [, ])
+ // from being interpreted by eqn.
+ prints(". ds " NOP_NAME " \\\" empty\n");
+ prints(". ig " NOP_NAME "\n"
+ ".EQ\n"
+ "delim off\n"
+ ".EN\n"
+ ". " NOP_NAME "\n");
+ entry_list->set_location();
+ prints(". nr " PREVIOUS_PAGE_REG " (\\n% - 1)\n"
+ ". tmc \\n[.F]:\\n[.c]: error:\n"
+ ". tmc \" boxed table does not fit on page"
+ " \\n[" PREVIOUS_PAGE_REG "];\n"
+ ". tm1 \" use .TS H/.TH with a supporting macro package"
+ "\n"
+ ". rr " PREVIOUS_PAGE_REG "\n");
+ prints(". ig " NOP_NAME "\n"
+ ".EQ\n"
+ "delim on\n"
+ ".EN\n"
+ ". " NOP_NAME "\n");
+ prints(". \\}\n"
+ ". el \\{\\\n"
+ ". 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");
+ prints(".nr " SAVED_NUMBERING_LINENO " \\n[ln]\n"
+ ".nr ln 0\n"
+ ".nr " SAVED_NUMBERING_SUPPRESSION_COUNT " \\n[.nn]\n"
+ ".nn 2147483647\n"); // 2^31-1; inelegant but effective
+ prints(".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 not 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 "] \\{\\\n");
+ 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] || expand[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, int do_expand)
+{
+ assert(end_col > start_col);
+ int i;
+ for (i = start_col; i <= end_col; i++)
+ if (expand[i])
+ break;
+ if (i > end_col) {
+ if (do_expand)
+ return;
+ }
+ else {
+ if (!do_expand)
+ return;
+ }
+ printfs(".nr %1 \\n[%2]",
+ span_width_reg(start_col, end_col),
+ span_width_reg(start_col, start_col));
+ for (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)
+: next(p), start_col(sc), end_col(ec)
+{
+}
+
+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_overall_width()
+{
+ prints(".\\\" compute overall width\n");
+ if (!(flags & GAP_EXPAND)) {
+ if (left_separation)
+ printfs(".if n .ll -%1n\n", as_string(left_separation));
+ if (right_separation)
+ printfs(".if n .ll -%1n\n", as_string(right_separation));
+ }
+ // Compute the amount of horizontal space available for expansion,
+ // measuring every column _including_ those eligible for expansion.
+ // This is the minimum required to set the table without compression.
+ prints(".nr " EXPAND_REG " 0\n");
+ prints(".nr " AVAILABLE_WIDTH_REG " \\n[.l]-\\n[.i]");
+ for (int i = 0; i < ncolumns; i++)
+ printfs("-\\n[%1]", span_width_reg(i, i));
+ if (total_separation)
+ printfs("-%1n", as_string(total_separation));
+ prints("\n");
+ // If the "expand" region option was given, a different warning will
+ // be issued later (if "nowarn" was not also specified).
+ if ((!(flags & NOWARN)) && (!(flags & EXPAND))) {
+ prints(".if \\n[" AVAILABLE_WIDTH_REG "]<0 \\{\\\n");
+ // Protect characters in diagnostic message (especially :, [, ])
+ // from being interpreted by eqn.
+ prints(". ig\n"
+ ".EQ\n"
+ "delim off\n"
+ ".EN\n"
+ ". .\n");
+ entry_list->set_location();
+ prints(". tmc \\n[.F]:\\n[.c]: warning:\n"
+ ". tm1 \" table wider than line length minus indentation"
+ "\n");
+ prints(". ig\n"
+ ".EQ\n"
+ "delim on\n"
+ ".EN\n"
+ ". .\n");
+ prints(". nr " AVAILABLE_WIDTH_REG " 0\n");
+ prints(".\\}\n");
+ }
+ // Now do a similar computation, this time omitting columns that
+ // _aren't_ undergoing expansion. The difference is the amount of
+ // space we have to distribute among the expanded columns.
+ bool do_expansion = false;
+ for (int i = 0; i < ncolumns; i++)
+ if (expand[i]) {
+ do_expansion = true;
+ break;
+ }
+ if (do_expansion) {
+ prints(".if \\n[" AVAILABLE_WIDTH_REG "] \\\n");
+ prints(". nr " EXPAND_REG " \\n[.l]-\\n[.i]");
+ for (int i = 0; i < ncolumns; i++)
+ if (!expand[i])
+ printfs("-\\n[%1]", span_width_reg(i, i));
+ if (total_separation)
+ printfs("-%1n", as_string(total_separation));
+ prints("\n");
+ int colcount = count_expand_columns();
+ if (colcount > 1)
+ printfs(".nr " EXPAND_REG " \\n[" EXPAND_REG "]/%1\n",
+ as_string(colcount));
+ for (int i = 0; i < ncolumns; i++)
+ if (expand[i])
+ printfs(".nr %1 \\n[%1]>?\\n[" EXPAND_REG "]\n",
+ span_width_reg(i, i));
+ }
+}
+
+void table::compute_total_separation()
+{
+ if (flags & (ALLBOX | BOX | DOUBLEBOX))
+ left_separation = right_separation = 1;
+ else {
+ for (int r = 0; r < nrows; r++) {
+ if (vline[r][0] > 0)
+ left_separation = 1;
+ if (vline[r][ncolumns] > 0)
+ right_separation = 1;
+ }
+ }
+ total_separation = left_separation + right_separation;
+ for (int c = 0; c < ncolumns - 1; c++)
+ total_separation += column_separation[c];
+}
+
+void table::compute_separation_factor()
+{
+ prints(".\\\" compute column separation factor\n");
+ // Don't let the separation factor be negative.
+ prints(".nr " SEPARATION_FACTOR_REG " \\n[.l]-\\n[.i]");
+ for (int i = 0; i < ncolumns; i++)
+ printfs("-\\n[%1]", span_width_reg(i, i));
+ printfs("/%1\n", as_string(total_separation));
+ // Store the remainder for use in compute_column_positions().
+ if (flags & GAP_EXPAND) {
+ prints(".if n \\\n");
+ prints(". nr " LEFTOVER_FACTOR_REG " \\n[.l]-\\n[.i]");
+ for (int i = 0; i < ncolumns; i++)
+ printfs("-\\n[%1]", span_width_reg(i, i));
+ printfs("%%%1\n", as_string(total_separation));
+ }
+ prints(".ie \\n[" SEPARATION_FACTOR_REG "]<=0 \\{\\\n");
+ if (!(flags & NOWARN)) {
+ // Protect characters in diagnostic message (especially :, [, ])
+ // from being interpreted by eqn.
+ prints(".ig\n"
+ ".EQ\n"
+ "delim off\n"
+ ".EN\n"
+ "..\n");
+ entry_list->set_location();
+ prints(".tmc \\n[.F]:\\n[.c]: warning:\n"
+ ".tm1 \" table column separation reduced to zero\n"
+ ".nr " SEPARATION_FACTOR_REG " 0\n");
+ }
+ prints(".\\}\n"
+ ".el .if \\n[" SEPARATION_FACTOR_REG "]<1n \\{\\\n");
+ if (!(flags & NOWARN)) {
+ entry_list->set_location();
+ prints(".tmc \\n[.F]:\\n[.c]: warning:\n"
+ ".tm1 \" table column separation reduced to fit line"
+ " length\n");
+ prints(".ig\n"
+ ".EQ\n"
+ "delim on\n"
+ ".EN\n"
+ "..\n");
+ }
+ prints(".\\}\n");
+}
+
+void table::compute_column_positions()
+{
+ prints(".\\\" compute column positions\n");
+ printfs(".nr %1 0\n", column_divide_reg(0));
+ printfs(".nr %1 %2n\n", column_start_reg(0),
+ as_string(left_separation));
+ // In nroff mode, compensate for width of vertical rule.
+ if (left_separation)
+ printfs(".if n .nr %1 +1n\n", column_start_reg(0));
+ 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]));
+ // If we have leftover expansion room in a table using the "expand"
+ // region option, put it prior to the last column so that the table
+ // looks as if expanded to the available line length.
+ if ((ncolumns > 2) && (flags & GAP_EXPAND) && (i == (ncolumns - 1)))
+ printfs(".if n .if \\n[" LEFTOVER_FACTOR_REG "] .nr %1 +(1n>?\\n["
+ LEFTOVER_FACTOR_REG "])\n",
+ column_start_reg(i));
+ 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]+%3n\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()
+{
+ prints(".\\\" compute column widths\n");
+ build_span_list();
+ int i;
+ horizontal_span *p;
+ // These values get refined later.
+ 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 (n;%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);
+ // Compute all field widths except for blocks.
+ table_entry *q;
+ for (q = entry_list; q; q = q->next)
+ if (!q->mod->zero_width)
+ q->do_width();
+ // Compute all span widths, not handling blocks yet.
+ 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);
+ // Making columns equal normally increases the width of some columns.
+ make_columns_equal();
+ // Note that divide_span keeps equal width columns equal.
+ // This function might increase the width of some columns, too.
+ for (p = span_list; p; p = p->next)
+ divide_span(p->start_col, p->end_col);
+ compute_total_separation();
+ for (p = span_list; p; p = p->next)
+ sum_columns(p->start_col, p->end_col, 0);
+ // Now handle unexpanded blocks.
+ bool had_spanning_block = false;
+ bool had_equal_block = false;
+ for (q = entry_list; q; q = q->next)
+ if (q->divert(ncolumns, minimum_width,
+ (flags & EXPAND) ? column_separation : 0, 0)) {
+ if (q->end_col > q->start_col)
+ had_spanning_block = true;
+ for (i = q->start_col; i <= q->end_col && !had_equal_block; i++)
+ if (equal[i])
+ had_equal_block = true;
+ }
+ // Adjust widths.
+ 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_overall_width();
+ if ((flags & EXPAND) && total_separation != 0) {
+ compute_separation_factor();
+ for (p = span_list; p; p = p->next)
+ sum_columns(p->start_col, p->end_col, 0);
+ }
+ else {
+ // Handle expanded blocks.
+ for (p = span_list; p; p = p->next)
+ sum_columns(p->start_col, p->end_col, 1);
+ for (q = entry_list; q; q = q->next)
+ if (q->divert(ncolumns, minimum_width, 0, 1)) {
+ if (q->end_col > q->start_col)
+ had_spanning_block = true;
+ }
+ // Adjust widths again.
+ if (had_spanning_block)
+ for (p = span_list; p; p = p->next)
+ divide_span(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(".\\\" define bottom macro\n");
+ prints(".eo\n"
+ // protect # in macro name against eqn
+ ".ig\n"
+ ".EQ\n"
+ "delim off\n"
+ ".EN\n"
+ "..\n"
+ ".de T#\n"
+ ". if !\\n[" SUPPRESS_BOTTOM_REG "] \\{\\\n"
+ ". " 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 "] \\{\\\n");
+ 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.] \\{\\\n"
+ ". 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");
+ if ((flags & NOKEEP) && (flags & (BOX | DOUBLEBOX | ALLBOX)))
+ prints(". if (\\n% > \\n[" STARTING_PAGE_REG "]) \\{\\\n"
+ ". tmc \\n[.F]:\\n[.c]: warning:\n"
+ ". tmc \" boxed, unkept table does not fit on page\n"
+ ". tm1 \" \\n[" STARTING_PAGE_REG "]\n"
+ ". \\}\n");
+ prints(". \\}\n"
+ "..\n"
+ ".ig\n"
+ ".EQ\n"
+ "delim on\n"
+ ".EN\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)
+{
+ printfs(".\\\" do row %1\n", i_to_a(r));
+ if (!(flags & NOKEEP) && row_begins_section(r))
+ prints(".if \\n[" USE_KEEPS_REG "] ." KEEP_MACRO_NAME "\n");
+ bool had_line = false;
+ 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 = true;
+ break;
+ }
+ if (!had_line && !row_is_all_lines[r])
+ printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r));
+ had_line = false;
+ 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 = true;
+ }
+ }
+ // 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(".if \\n[" USE_KEEPS_REG "] ." 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) {
+ if (e->end_row != e->start_row) {
+ prints('\n');
+ simple->position_vertically();
+ prints("\\&");
+ }
+ 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 *vr = vrule_list; vr; vr = vr->next)
+ if (vr->end_row == r) {
+ if (!printed_one) {
+ prints("." REPEATED_VPT_MACRO " 0\n");
+ printed_one = 1;
+ }
+ vr->print();
+ }
+ if (printed_one)
+ prints("." REPEATED_VPT_MACRO " 1\n");
+ if (!(flags & NOKEEP) && row_ends_section(r))
+ prints(".if \\n[" USE_KEEPS_REG "] ." RELEASE_MACRO_NAME "\n");
+ }
+}
+
+void table::do_top()
+{
+ prints(".\\\" do top\n");
+ prints(".ss \\n[" SAVED_INTER_WORD_SPACE_SIZE "]\n");
+ prints(".fc \002\003\n");
+ if (flags & (BOX | DOUBLEBOX | ALLBOX))
+ prints(".nr " IS_BOXED_REG " 1\n");
+ else
+ prints(".nr " IS_BOXED_REG " 0\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);
+ // On terminal devices, a vertical rule on the first row of the table
+ // will stick out 1v above it if it the table is unboxed or lacks a
+ // horizontal rule on the first row. This is necessary for grotty's
+ // rule intersection detection. We must make room for it so that the
+ // vertical rule is not drawn above the top of the page.
+ else if ((flags & HAS_TOP_VLINE) && !(flags & HAS_TOP_HLINE))
+ prints(".if n .sp\n");
+ prints(".nr " STARTING_PAGE_REG " \\n%\n");
+ //printfs(".mk %1\n", row_top_reg(0));
+}
+
+void table::do_bottom()
+{
+ prints(".\\\" do bottom\n");
+ // 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(".if \\n[" USE_KEEPS_REG "] ." RELEASE_MACRO_NAME "\n");
+ printfs(".mk %1\n", row_top_reg(nrows));
+ prints(".nr " NEED_BOTTOM_RULE_REG " 1\n"
+ ".nr T. 1\n"
+ // protect # in macro name against eqn
+ ".ig\n"
+ ".EQ\n"
+ "delim off\n"
+ ".EN\n"
+ "..\n"
+ ".T#\n"
+ ".ig\n"
+ ".EQ\n"
+ "delim on\n"
+ ".EN\n"
+ "..\n");
+ if (!(flags & NOKEEP) && (flags & (BOX | DOUBLEBOX | ALLBOX)))
+ prints("." TABLE_RELEASE_MACRO_NAME "\n");
+ if (flags & DOUBLEBOX)
+ prints(".sp " DOUBLE_LINE_SEP "\n");
+ // Horizontal box lines take up an entire row on nroff devices (maybe
+ // a half-row if we ever support [emulators of] devices like the
+ // Teletype Model 37 with half-line motions).
+ if (flags & (BOX | DOUBLEBOX | ALLBOX))
+ prints(".if n .sp\n");
+ // Space again for the doublebox option, until we can draw that more
+ // attractively; see Savannah #43637.
+ if (flags & DOUBLEBOX)
+ prints(".if n .sp\n");
+ prints("." RESET_MACRO_NAME "\n"
+ ".nn \\n[" SAVED_NUMBERING_SUPPRESSION_COUNT "]\n"
+ ".ie \\n[" SAVED_NUMBERING_LINENO "] "
+ ".nm \\n[" SAVED_NUMBERING_LINENO "]\n"
+ ".el .nm\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 {
+ string filename(fn);
+ filename += '\0';
+ normalize_for_lf(filename);
+ printfs(".lf %1 %2\n", as_string(ln), filename.contents());
+ 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 == "printfs format character not in [1-9%]");
+ }
+ }
+ else
+ prints(c);
+ }
+ }
+}
+
+// Local Variables:
+// fill-column: 72
+// mode: C++
+// End:
+// vim: set cindent noexpandtab shiftwidth=2 textwidth=72: