* src/utils/pfbtops/pfbtops.c (get_text): Handle loop counter
[s-roff.git] / src / preproc / tbl / table.cpp
blob32068c367ed1c435b9a3bf691b2504b2fd24ea70
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2003
3 Free Software Foundation, Inc.
4 Written by James Clark (jjc@jclark.com)
6 This file is part of groff.
8 groff is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 2, or (at your option) any later
11 version.
13 groff is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 for more details.
18 You should have received a copy of the GNU General Public License along
19 with groff; see the file COPYING. If not, write to the Free Software
20 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
22 #include "table.h"
24 #define BAR_HEIGHT ".25m"
25 #define DOUBLE_LINE_SEP "2p"
26 #define HALF_DOUBLE_LINE_SEP "1p"
27 #define LINE_SEP "2p"
28 #define BODY_DEPTH ".25m"
30 const int DEFAULT_COLUMN_SEPARATION = 3;
32 #define DELIMITER_CHAR "\\[tbl]"
33 #define PREFIX "3"
34 #define SEPARATION_FACTOR_REG PREFIX "sep"
35 #define BOTTOM_REG PREFIX "bot"
36 #define RESET_MACRO_NAME PREFIX "init"
37 #define LINESIZE_REG PREFIX "lps"
38 #define TOP_REG PREFIX "top"
39 #define CURRENT_ROW_REG PREFIX "crow"
40 #define LAST_PASSED_ROW_REG PREFIX "passed"
41 #define TRANSPARENT_STRING_NAME PREFIX "trans"
42 #define QUOTE_STRING_NAME PREFIX "quote"
43 #define SECTION_DIVERSION_NAME PREFIX "section"
44 #define SECTION_DIVERSION_FLAG_REG PREFIX "sflag"
45 #define SAVED_VERTICAL_POS_REG PREFIX "vert"
46 #define NEED_BOTTOM_RULE_REG PREFIX "brule"
47 #define KEEP_MACRO_NAME PREFIX "keep"
48 #define RELEASE_MACRO_NAME PREFIX "release"
49 #define SAVED_FONT_REG PREFIX "fnt"
50 #define SAVED_SIZE_REG PREFIX "sz"
51 #define SAVED_FILL_REG PREFIX "fll"
52 #define SAVED_INDENT_REG PREFIX "ind"
53 #define SAVED_CENTER_REG PREFIX "cent"
54 #define TABLE_DIVERSION_NAME PREFIX "table"
55 #define TABLE_DIVERSION_FLAG_REG PREFIX "tflag"
56 #define TABLE_KEEP_MACRO_NAME PREFIX "tkeep"
57 #define TABLE_RELEASE_MACRO_NAME PREFIX "trelease"
58 #define NEEDED_REG PREFIX "needed"
59 #define REPEATED_MARK_MACRO PREFIX "rmk"
60 #define REPEATED_VPT_MACRO PREFIX "rvpt"
61 #define SUPPRESS_BOTTOM_REG PREFIX "supbot"
62 #define SAVED_DN_REG PREFIX "dn"
64 // this must be one character
65 #define COMPATIBLE_REG PREFIX "c"
67 #define BLOCK_WIDTH_PREFIX PREFIX "tbw"
68 #define BLOCK_DIVERSION_PREFIX PREFIX "tbd"
69 #define BLOCK_HEIGHT_PREFIX PREFIX "tbh"
70 #define SPAN_WIDTH_PREFIX PREFIX "w"
71 #define SPAN_LEFT_NUMERIC_WIDTH_PREFIX PREFIX "lnw"
72 #define SPAN_RIGHT_NUMERIC_WIDTH_PREFIX PREFIX "rnw"
73 #define SPAN_ALPHABETIC_WIDTH_PREFIX PREFIX "aw"
74 #define COLUMN_SEPARATION_PREFIX PREFIX "cs"
75 #define ROW_START_PREFIX PREFIX "rs"
76 #define COLUMN_START_PREFIX PREFIX "cl"
77 #define COLUMN_END_PREFIX PREFIX "ce"
78 #define COLUMN_DIVIDE_PREFIX PREFIX "cd"
79 #define ROW_TOP_PREFIX PREFIX "rt"
81 string block_width_reg(int r, int c);
82 string block_diversion_name(int r, int c);
83 string block_height_reg(int r, int c);
84 string span_width_reg(int start_col, int end_col);
85 string span_left_numeric_width_reg(int start_col, int end_col);
86 string span_right_numeric_width_reg(int start_col, int end_col);
87 string span_alphabetic_width_reg(int start_col, int end_col);
88 string column_separation_reg(int col);
89 string row_start_reg(int r);
90 string column_start_reg(int c);
91 string column_end_reg(int c);
92 string column_divide_reg(int c);
93 string row_top_reg(int r);
95 void set_inline_modifier(const entry_modifier *);
96 void restore_inline_modifier(const entry_modifier *m);
97 void set_modifier(const entry_modifier *);
98 int find_decimal_point(const char *s, char decimal_point_char,
99 const char *delim);
101 string an_empty_string;
102 int location_force_filename = 0;
104 void printfs(const char *,
105 const string &arg1 = an_empty_string,
106 const string &arg2 = an_empty_string,
107 const string &arg3 = an_empty_string,
108 const string &arg4 = an_empty_string,
109 const string &arg5 = an_empty_string);
111 void prints(const string &);
113 inline void prints(char c)
115 putchar(c);
118 inline void prints(const char *s)
120 fputs(s, stdout);
123 void prints(const string &s)
125 if (!s.empty())
126 fwrite(s.contents(), 1, s.length(), stdout);
129 struct horizontal_span {
130 horizontal_span *next;
131 short start_col;
132 short end_col;
133 horizontal_span(int, int, horizontal_span *);
136 struct single_line_entry;
137 struct double_line_entry;
138 struct simple_entry;
140 class table_entry {
141 friend class table;
142 table_entry *next;
143 int input_lineno;
144 const char *input_filename;
145 protected:
146 int start_row;
147 int end_row;
148 short start_col;
149 short end_col;
150 const entry_modifier *mod;
151 public:
152 void set_location();
153 table_entry(const entry_modifier *);
154 virtual ~table_entry();
155 virtual int divert(int ncols, const string *mw, int *sep);
156 virtual void do_width();
157 virtual void do_depth();
158 virtual void print() = 0;
159 virtual void position_vertically() = 0;
160 virtual single_line_entry *to_single_line_entry();
161 virtual double_line_entry *to_double_line_entry();
162 virtual simple_entry *to_simple_entry();
163 virtual int line_type();
164 virtual void note_double_vrule_on_right(int);
165 virtual void note_double_vrule_on_left(int);
168 class simple_entry : public table_entry {
169 public:
170 simple_entry(const entry_modifier *);
171 void print();
172 void position_vertically();
173 simple_entry *to_simple_entry();
174 virtual void add_tab();
175 virtual void simple_print(int);
178 class empty_entry : public simple_entry {
179 public:
180 empty_entry(const entry_modifier *);
181 int line_type();
184 class text_entry : public simple_entry {
185 protected:
186 char *contents;
187 void print_contents();
188 public:
189 text_entry(char *, const entry_modifier *);
190 ~text_entry();
193 void text_entry::print_contents()
195 set_inline_modifier(mod);
196 prints(contents);
197 restore_inline_modifier(mod);
200 class repeated_char_entry : public text_entry {
201 public:
202 repeated_char_entry(char *s, const entry_modifier *m);
203 void simple_print(int);
206 class simple_text_entry : public text_entry {
207 public:
208 simple_text_entry(char *s, const entry_modifier *m);
209 void do_width();
212 class left_text_entry : public simple_text_entry {
213 public:
214 left_text_entry(char *s, const entry_modifier *m);
215 void simple_print(int);
216 void add_tab();
219 class right_text_entry : public simple_text_entry {
220 public:
221 right_text_entry(char *s, const entry_modifier *m);
222 void simple_print(int);
223 void add_tab();
226 class center_text_entry : public simple_text_entry {
227 public:
228 center_text_entry(char *s, const entry_modifier *m);
229 void simple_print(int);
230 void add_tab();
233 class numeric_text_entry : public text_entry {
234 int dot_pos;
235 public:
236 numeric_text_entry(char *s, const entry_modifier *m, int pos);
237 void do_width();
238 void simple_print(int);
241 class alphabetic_text_entry : public text_entry {
242 public:
243 alphabetic_text_entry(char *s, const entry_modifier *m);
244 void do_width();
245 void simple_print(int);
246 void add_tab();
249 class line_entry : public simple_entry {
250 protected:
251 char double_vrule_on_right;
252 char double_vrule_on_left;
253 public:
254 line_entry(const entry_modifier *);
255 void note_double_vrule_on_right(int);
256 void note_double_vrule_on_left(int);
257 void simple_print(int) = 0;
260 class single_line_entry : public line_entry {
261 public:
262 single_line_entry(const entry_modifier *m);
263 void simple_print(int);
264 single_line_entry *to_single_line_entry();
265 int line_type();
268 class double_line_entry : public line_entry {
269 public:
270 double_line_entry(const entry_modifier *m);
271 void simple_print(int);
272 double_line_entry *to_double_line_entry();
273 int line_type();
276 class short_line_entry : public simple_entry {
277 public:
278 short_line_entry(const entry_modifier *m);
279 void simple_print(int);
280 int line_type();
283 class short_double_line_entry : public simple_entry {
284 public:
285 short_double_line_entry(const entry_modifier *m);
286 void simple_print(int);
287 int line_type();
290 class block_entry : public table_entry {
291 char *contents;
292 protected:
293 void do_divert(int alphabetic, int ncols, const string *mw, int *sep);
294 public:
295 block_entry(char *s, const entry_modifier *m);
296 ~block_entry();
297 int divert(int ncols, const string *mw, int *sep);
298 void do_width();
299 void do_depth();
300 void position_vertically();
301 void print() = 0;
304 class left_block_entry : public block_entry {
305 public:
306 left_block_entry(char *s, const entry_modifier *m);
307 void print();
310 class right_block_entry : public block_entry {
311 public:
312 right_block_entry(char *s, const entry_modifier *m);
313 void print();
316 class center_block_entry : public block_entry {
317 public:
318 center_block_entry(char *s, const entry_modifier *m);
319 void print();
322 class alphabetic_block_entry : public block_entry {
323 public:
324 alphabetic_block_entry(char *s, const entry_modifier *m);
325 void print();
326 int divert(int ncols, const string *mw, int *sep);
329 table_entry::table_entry(const entry_modifier *m)
330 : next(0), input_lineno(-1), input_filename(0),
331 start_row(-1), end_row(-1), start_col(-1), end_col(-1), mod(m)
335 table_entry::~table_entry()
339 int table_entry::divert(int, const string *, int *)
341 return 0;
344 void table_entry::do_width()
348 single_line_entry *table_entry::to_single_line_entry()
350 return 0;
353 double_line_entry *table_entry::to_double_line_entry()
355 return 0;
358 simple_entry *table_entry::to_simple_entry()
360 return 0;
363 void table_entry::do_depth()
367 void table_entry::set_location()
369 set_troff_location(input_filename, input_lineno);
372 int table_entry::line_type()
374 return -1;
377 void table_entry::note_double_vrule_on_right(int)
381 void table_entry::note_double_vrule_on_left(int)
385 simple_entry::simple_entry(const entry_modifier *m) : table_entry(m)
389 void simple_entry::add_tab()
391 // do nothing
394 void simple_entry::simple_print(int)
396 // do nothing
399 void simple_entry::position_vertically()
401 if (start_row != end_row)
402 switch (mod->vertical_alignment) {
403 case entry_modifier::TOP:
404 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
405 break;
406 case entry_modifier::CENTER:
407 // Peform the motion in two stages so that the center is rounded
408 // vertically upwards even if net vertical motion is upwards.
409 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
410 printfs(".sp \\n[" BOTTOM_REG "]u-\\n[%1]u-1v/2u\n",
411 row_start_reg(start_row));
412 break;
413 case entry_modifier::BOTTOM:
414 printfs(".sp |\\n[%1]u+\\n[" BOTTOM_REG "]u-\\n[%1]u-1v\n",
415 row_start_reg(start_row));
416 break;
417 default:
418 assert(0);
422 void simple_entry::print()
424 prints(".ta");
425 add_tab();
426 prints('\n');
427 set_location();
428 prints("\\&");
429 simple_print(0);
430 prints('\n');
433 simple_entry *simple_entry::to_simple_entry()
435 return this;
438 empty_entry::empty_entry(const entry_modifier *m)
439 : simple_entry(m)
443 int empty_entry::line_type()
445 return 0;
448 text_entry::text_entry(char *s, const entry_modifier *m)
449 : simple_entry(m), contents(s)
453 text_entry::~text_entry()
455 a_delete contents;
458 repeated_char_entry::repeated_char_entry(char *s, const entry_modifier *m)
459 : text_entry(s, m)
463 void repeated_char_entry::simple_print(int)
465 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
466 set_inline_modifier(mod);
467 printfs("\\l" DELIMITER_CHAR "\\n[%1]u\\&",
468 span_width_reg(start_col, end_col));
469 prints(contents);
470 prints(DELIMITER_CHAR);
471 restore_inline_modifier(mod);
474 simple_text_entry::simple_text_entry(char *s, const entry_modifier *m)
475 : text_entry(s, m)
479 void simple_text_entry::do_width()
481 set_location();
482 printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR,
483 span_width_reg(start_col, end_col));
484 print_contents();
485 prints(DELIMITER_CHAR "\n");
488 left_text_entry::left_text_entry(char *s, const entry_modifier *m)
489 : simple_text_entry(s, m)
493 void left_text_entry::simple_print(int)
495 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
496 print_contents();
499 // The only point of this is to make `\a' ``work'' as in Unix tbl. Grrr.
501 void left_text_entry::add_tab()
503 printfs(" \\n[%1]u", column_end_reg(end_col));
506 right_text_entry::right_text_entry(char *s, const entry_modifier *m)
507 : simple_text_entry(s, m)
511 void right_text_entry::simple_print(int)
513 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
514 prints("\002\003");
515 print_contents();
516 prints("\002");
519 void right_text_entry::add_tab()
521 printfs(" \\n[%1]u", column_end_reg(end_col));
524 center_text_entry::center_text_entry(char *s, const entry_modifier *m)
525 : simple_text_entry(s, m)
529 void center_text_entry::simple_print(int)
531 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
532 prints("\002\003");
533 print_contents();
534 prints("\003\002");
537 void center_text_entry::add_tab()
539 printfs(" \\n[%1]u", column_end_reg(end_col));
542 numeric_text_entry::numeric_text_entry(char *s, const entry_modifier *m, int pos)
543 : text_entry(s, m), dot_pos(pos)
547 void numeric_text_entry::do_width()
549 if (dot_pos != 0) {
550 set_location();
551 printfs(".nr %1 0\\w" DELIMITER_CHAR,
552 block_width_reg(start_row, start_col));
553 set_inline_modifier(mod);
554 for (int i = 0; i < dot_pos; i++)
555 prints(contents[i]);
556 restore_inline_modifier(mod);
557 prints(DELIMITER_CHAR "\n");
558 printfs(".nr %1 \\n[%1]>?\\n[%2]\n",
559 span_left_numeric_width_reg(start_col, end_col),
560 block_width_reg(start_row, start_col));
562 else
563 printfs(".nr %1 0\n", block_width_reg(start_row, start_col));
564 if (contents[dot_pos] != '\0') {
565 set_location();
566 printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR,
567 span_right_numeric_width_reg(start_col, end_col));
568 set_inline_modifier(mod);
569 prints(contents + dot_pos);
570 restore_inline_modifier(mod);
571 prints(DELIMITER_CHAR "\n");
575 void numeric_text_entry::simple_print(int)
577 printfs("\\h'|(\\n[%1]u-\\n[%2]u-\\n[%3]u/2u+\\n[%2]u+\\n[%4]u-\\n[%5]u)'",
578 span_width_reg(start_col, end_col),
579 span_left_numeric_width_reg(start_col, end_col),
580 span_right_numeric_width_reg(start_col, end_col),
581 column_start_reg(start_col),
582 block_width_reg(start_row, start_col));
583 print_contents();
586 alphabetic_text_entry::alphabetic_text_entry(char *s, const entry_modifier *m)
587 : text_entry(s, m)
591 void alphabetic_text_entry::do_width()
593 set_location();
594 printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR,
595 span_alphabetic_width_reg(start_col, end_col));
596 print_contents();
597 prints(DELIMITER_CHAR "\n");
600 void alphabetic_text_entry::simple_print(int)
602 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
603 printfs("\\h'\\n[%1]u-\\n[%2]u/2u'",
604 span_width_reg(start_col, end_col),
605 span_alphabetic_width_reg(start_col, end_col));
606 print_contents();
609 // The only point of this is to make `\a' ``work'' as in Unix tbl. Grrr.
611 void alphabetic_text_entry::add_tab()
613 printfs(" \\n[%1]u", column_end_reg(end_col));
616 block_entry::block_entry(char *s, const entry_modifier *m)
617 : table_entry(m), contents(s)
621 block_entry::~block_entry()
623 a_delete contents;
626 void block_entry::position_vertically()
628 if (start_row != end_row)
629 switch(mod->vertical_alignment) {
630 case entry_modifier::TOP:
631 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
632 break;
633 case entry_modifier::CENTER:
634 // Peform the motion in two stages so that the center is rounded
635 // vertically upwards even if net vertical motion is upwards.
636 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
637 printfs(".sp \\n[" BOTTOM_REG "]u-\\n[%1]u-\\n[%2]u/2u\n",
638 row_start_reg(start_row),
639 block_height_reg(start_row, start_col));
640 break;
641 case entry_modifier::BOTTOM:
642 printfs(".sp |\\n[%1]u+\\n[" BOTTOM_REG "]u-\\n[%1]u-\\n[%2]u\n",
643 row_start_reg(start_row),
644 block_height_reg(start_row, start_col));
645 break;
646 default:
647 assert(0);
649 if (mod->stagger)
650 prints(".sp -.5v\n");
653 int block_entry::divert(int ncols, const string *mw, int *sep)
655 do_divert(0, ncols, mw, sep);
656 return 1;
659 void block_entry::do_divert(int alphabetic, int ncols, const string *mw,
660 int *sep)
662 printfs(".di %1\n", block_diversion_name(start_row, start_col));
663 prints(".if \\n[" SAVED_FILL_REG "] .fi\n"
664 ".in 0\n");
665 prints(".ll ");
666 int i;
667 for (i = start_col; i <= end_col; i++)
668 if (mw[i].empty())
669 break;
670 if (i > end_col) {
671 // Every column spanned by this entry has a minimum width.
672 for (int j = start_col; j <= end_col; j++) {
673 if (j > start_col) {
674 if (sep)
675 printfs("+%1n", as_string(sep[j - 1]));
676 prints('+');
678 printfs("(n;%1)", mw[j]);
680 printfs(">?\\n[%1]u", span_width_reg(start_col, end_col));
682 else
683 printfs("(u;\\n[%1]>?(\\n[.l]*%2/%3))",
684 span_width_reg(start_col, end_col),
685 as_string(end_col - start_col + 1),
686 as_string(ncols + 1));
687 if (alphabetic)
688 prints("-2n");
689 prints("\n");
690 set_modifier(mod);
691 prints(".cp \\n(" COMPATIBLE_REG "\n");
692 set_location();
693 prints(contents);
694 prints(".br\n.di\n.cp 0\n");
695 if (!mod->zero_width) {
696 if (alphabetic) {
697 printfs(".nr %1 \\n[%1]>?(\\n[dl]+2n)\n",
698 span_width_reg(start_col, end_col));
699 printfs(".nr %1 \\n[%1]>?\\n[dl]\n",
700 span_alphabetic_width_reg(start_col, end_col));
702 else
703 printfs(".nr %1 \\n[%1]>?\\n[dl]\n", span_width_reg(start_col, end_col));
705 printfs(".nr %1 \\n[dn]\n", block_height_reg(start_row, start_col));
706 printfs(".nr %1 \\n[dl]\n", block_width_reg(start_row, start_col));
707 prints("." RESET_MACRO_NAME "\n"
708 ".in \\n[" SAVED_INDENT_REG "]u\n"
709 ".nf\n");
710 // the block might have contained .lf commands
711 location_force_filename = 1;
714 void block_entry::do_width()
716 // do nothing; the action happens in divert
719 void block_entry::do_depth()
721 printfs(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?(\\n[%1]+\\n[%2])\n",
722 row_start_reg(start_row),
723 block_height_reg(start_row, start_col));
726 left_block_entry::left_block_entry(char *s, const entry_modifier *m)
727 : block_entry(s, m)
731 void left_block_entry::print()
733 printfs(".in +\\n[%1]u\n", column_start_reg(start_col));
734 printfs(".%1\n", block_diversion_name(start_row, start_col));
735 prints(".in\n");
738 right_block_entry::right_block_entry(char *s, const entry_modifier *m)
739 : block_entry(s, m)
743 void right_block_entry::print()
745 printfs(".in +\\n[%1]u+\\n[%2]u-\\n[%3]u\n",
746 column_start_reg(start_col),
747 span_width_reg(start_col, end_col),
748 block_width_reg(start_row, start_col));
749 printfs(".%1\n", block_diversion_name(start_row, start_col));
750 prints(".in\n");
753 center_block_entry::center_block_entry(char *s, const entry_modifier *m)
754 : block_entry(s, m)
758 void center_block_entry::print()
760 printfs(".in +\\n[%1]u+(\\n[%2]u-\\n[%3]u/2u)\n",
761 column_start_reg(start_col),
762 span_width_reg(start_col, end_col),
763 block_width_reg(start_row, start_col));
764 printfs(".%1\n", block_diversion_name(start_row, start_col));
765 prints(".in\n");
768 alphabetic_block_entry::alphabetic_block_entry(char *s,
769 const entry_modifier *m)
770 : block_entry(s, m)
774 int alphabetic_block_entry::divert(int ncols, const string *mw, int *sep)
776 do_divert(1, ncols, mw, sep);
777 return 1;
780 void alphabetic_block_entry::print()
782 printfs(".in +\\n[%1]u+(\\n[%2]u-\\n[%3]u/2u)\n",
783 column_start_reg(start_col),
784 span_width_reg(start_col, end_col),
785 span_alphabetic_width_reg(start_col, end_col));
786 printfs(".%1\n", block_diversion_name(start_row, start_col));
787 prints(".in\n");
790 line_entry::line_entry(const entry_modifier *m)
791 : simple_entry(m), double_vrule_on_right(0), double_vrule_on_left(0)
795 void line_entry::note_double_vrule_on_right(int is_corner)
797 double_vrule_on_right = is_corner ? 1 : 2;
800 void line_entry::note_double_vrule_on_left(int is_corner)
802 double_vrule_on_left = is_corner ? 1 : 2;
805 single_line_entry::single_line_entry(const entry_modifier *m)
806 : line_entry(m)
810 int single_line_entry::line_type()
812 return 1;
815 void single_line_entry::simple_print(int dont_move)
817 printfs("\\h'|\\n[%1]u",
818 column_divide_reg(start_col));
819 if (double_vrule_on_left) {
820 prints(double_vrule_on_left == 1 ? "-" : "+");
821 prints(HALF_DOUBLE_LINE_SEP);
823 prints("'");
824 if (!dont_move)
825 prints("\\v'-" BAR_HEIGHT "'");
826 printfs("\\s[\\n[" LINESIZE_REG "]]" "\\D'l |\\n[%1]u",
827 column_divide_reg(end_col+1));
828 if (double_vrule_on_right) {
829 prints(double_vrule_on_left == 1 ? "+" : "-");
830 prints(HALF_DOUBLE_LINE_SEP);
832 prints("0'\\s0");
833 if (!dont_move)
834 prints("\\v'" BAR_HEIGHT "'");
837 single_line_entry *single_line_entry::to_single_line_entry()
839 return this;
842 double_line_entry::double_line_entry(const entry_modifier *m)
843 : line_entry(m)
847 int double_line_entry::line_type()
849 return 2;
852 void double_line_entry::simple_print(int dont_move)
854 if (!dont_move)
855 prints("\\v'-" BAR_HEIGHT "'");
856 printfs("\\h'|\\n[%1]u",
857 column_divide_reg(start_col));
858 if (double_vrule_on_left) {
859 prints(double_vrule_on_left == 1 ? "-" : "+");
860 prints(HALF_DOUBLE_LINE_SEP);
862 prints("'");
863 printfs("\\v'-" HALF_DOUBLE_LINE_SEP "'"
864 "\\s[\\n[" LINESIZE_REG "]]"
865 "\\D'l |\\n[%1]u",
866 column_divide_reg(end_col+1));
867 if (double_vrule_on_right)
868 prints("-" HALF_DOUBLE_LINE_SEP);
869 prints(" 0'");
870 printfs("\\v'" DOUBLE_LINE_SEP "'"
871 "\\D'l |\\n[%1]u",
872 column_divide_reg(start_col));
873 if (double_vrule_on_right) {
874 prints(double_vrule_on_left == 1 ? "+" : "-");
875 prints(HALF_DOUBLE_LINE_SEP);
877 prints(" 0'");
878 prints("\\s0"
879 "\\v'-" HALF_DOUBLE_LINE_SEP "'");
880 if (!dont_move)
881 prints("\\v'" BAR_HEIGHT "'");
884 double_line_entry *double_line_entry::to_double_line_entry()
886 return this;
889 short_line_entry::short_line_entry(const entry_modifier *m)
890 : simple_entry(m)
894 int short_line_entry::line_type()
896 return 1;
899 void short_line_entry::simple_print(int dont_move)
901 if (mod->stagger)
902 prints("\\v'-.5v'");
903 if (!dont_move)
904 prints("\\v'-" BAR_HEIGHT "'");
905 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
906 printfs("\\s[\\n[" LINESIZE_REG "]]"
907 "\\D'l \\n[%1]u 0'"
908 "\\s0",
909 span_width_reg(start_col, end_col));
910 if (!dont_move)
911 prints("\\v'" BAR_HEIGHT "'");
912 if (mod->stagger)
913 prints("\\v'.5v'");
916 short_double_line_entry::short_double_line_entry(const entry_modifier *m)
917 : simple_entry(m)
921 int short_double_line_entry::line_type()
923 return 2;
926 void short_double_line_entry::simple_print(int dont_move)
928 if (mod->stagger)
929 prints("\\v'-.5v'");
930 if (!dont_move)
931 prints("\\v'-" BAR_HEIGHT "'");
932 printfs("\\h'|\\n[%2]u'"
933 "\\v'-" HALF_DOUBLE_LINE_SEP "'"
934 "\\s[\\n[" LINESIZE_REG "]]"
935 "\\D'l \\n[%1]u 0'"
936 "\\v'" DOUBLE_LINE_SEP "'"
937 "\\D'l |\\n[%2]u 0'"
938 "\\s0"
939 "\\v'-" HALF_DOUBLE_LINE_SEP "'",
940 span_width_reg(start_col, end_col),
941 column_start_reg(start_col));
942 if (!dont_move)
943 prints("\\v'" BAR_HEIGHT "'");
944 if (mod->stagger)
945 prints("\\v'.5v'");
948 void set_modifier(const entry_modifier *m)
950 if (!m->font.empty())
951 printfs(".ft %1\n", m->font);
952 if (m->point_size.val != 0) {
953 prints(".ps ");
954 if (m->point_size.inc > 0)
955 prints('+');
956 else if (m->point_size.inc < 0)
957 prints('-');
958 printfs("%1\n", as_string(m->point_size.val));
960 if (m->vertical_spacing.val != 0) {
961 prints(".vs ");
962 if (m->vertical_spacing.inc > 0)
963 prints('+');
964 else if (m->vertical_spacing.inc < 0)
965 prints('-');
966 printfs("%1\n", as_string(m->vertical_spacing.val));
970 void set_inline_modifier(const entry_modifier *m)
972 if (!m->font.empty())
973 printfs("\\f[%1]", m->font);
974 if (m->point_size.val != 0) {
975 prints("\\s[");
976 if (m->point_size.inc > 0)
977 prints('+');
978 else if (m->point_size.inc < 0)
979 prints('-');
980 printfs("%1]", as_string(m->point_size.val));
982 if (m->stagger)
983 prints("\\v'-.5v'");
986 void restore_inline_modifier(const entry_modifier *m)
988 if (!m->font.empty())
989 prints("\\f[\\n[" SAVED_FONT_REG "]]");
990 if (m->point_size.val != 0)
991 prints("\\s[\\n[" SAVED_SIZE_REG "]]");
992 if (m->stagger)
993 prints("\\v'.5v'");
996 struct stuff {
997 stuff *next;
998 int row; // occurs before row `row'
999 char printed; // has it been printed?
1001 stuff(int);
1002 virtual void print(table *) = 0;
1003 virtual ~stuff();
1004 virtual int is_single_line() { return 0; };
1005 virtual int is_double_line() { return 0; };
1008 stuff::stuff(int r) : next(0), row(r), printed(0)
1012 stuff::~stuff()
1016 struct text_stuff : public stuff {
1017 string contents;
1018 const char *filename;
1019 int lineno;
1021 text_stuff(const string &, int r, const char *fn, int ln);
1022 ~text_stuff();
1023 void print(table *);
1026 text_stuff::text_stuff(const string &s, int r, const char *fn, int ln)
1027 : stuff(r), contents(s), filename(fn), lineno(ln)
1031 text_stuff::~text_stuff()
1035 void text_stuff::print(table *)
1037 printed = 1;
1038 prints(".cp \\n(" COMPATIBLE_REG "\n");
1039 set_troff_location(filename, lineno);
1040 prints(contents);
1041 prints(".cp 0\n");
1042 location_force_filename = 1; // it might have been a .lf command
1045 struct single_hline_stuff : public stuff {
1046 single_hline_stuff(int r);
1047 void print(table *);
1048 int is_single_line();
1051 single_hline_stuff::single_hline_stuff(int r) : stuff(r)
1055 void single_hline_stuff::print(table *tbl)
1057 printed = 1;
1058 tbl->print_single_hline(row);
1061 int single_hline_stuff::is_single_line()
1063 return 1;
1066 struct double_hline_stuff : stuff {
1067 double_hline_stuff(int r);
1068 void print(table *);
1069 int is_double_line();
1072 double_hline_stuff::double_hline_stuff(int r) : stuff(r)
1076 void double_hline_stuff::print(table *tbl)
1078 printed = 1;
1079 tbl->print_double_hline(row);
1082 int double_hline_stuff::is_double_line()
1084 return 1;
1087 struct vertical_rule {
1088 vertical_rule *next;
1089 int start_row;
1090 int end_row;
1091 short col;
1092 char is_double;
1093 string top_adjust;
1094 string bot_adjust;
1096 vertical_rule(int sr, int er, int c, int dbl, vertical_rule *);
1097 ~vertical_rule();
1098 void contribute_to_bottom_macro(table *);
1099 void print();
1102 vertical_rule::vertical_rule(int sr, int er, int c, int dbl, vertical_rule *p)
1103 : next(p), start_row(sr), end_row(er), col(c), is_double(dbl)
1107 vertical_rule::~vertical_rule()
1111 void vertical_rule::contribute_to_bottom_macro(table *tbl)
1113 printfs(".if \\n[" CURRENT_ROW_REG "]>=%1",
1114 as_string(start_row));
1115 if (end_row != tbl->get_nrows() - 1)
1116 printfs("&(\\n[" CURRENT_ROW_REG "]<%1)",
1117 as_string(end_row));
1118 prints(" \\{");
1119 printfs(".if %1<=\\n[" LAST_PASSED_ROW_REG "] .nr %2 \\n[#T]\n",
1120 as_string(start_row),
1121 row_top_reg(start_row));
1122 const char *offset_table[3];
1123 if (is_double) {
1124 offset_table[0] = "-" HALF_DOUBLE_LINE_SEP;
1125 offset_table[1] = "+" HALF_DOUBLE_LINE_SEP;
1126 offset_table[2] = 0;
1128 else {
1129 offset_table[0] = "";
1130 offset_table[1] = 0;
1132 for (const char **offsetp = offset_table; *offsetp; offsetp++) {
1133 prints(".sp -1\n"
1134 "\\v'" BODY_DEPTH);
1135 if (!bot_adjust.empty())
1136 printfs("+%1", bot_adjust);
1137 prints("'");
1138 printfs("\\h'\\n[%1]u%3'\\s[\\n[" LINESIZE_REG "]]\\D'l 0 |\\n[%2]u-1v",
1139 column_divide_reg(col),
1140 row_top_reg(start_row),
1141 *offsetp);
1142 if (!bot_adjust.empty())
1143 printfs("-(%1)", bot_adjust);
1144 // don't perform the top adjustment if the top is actually #T
1145 if (!top_adjust.empty())
1146 printfs("+((%1)*(%2>\\n[" LAST_PASSED_ROW_REG "]))",
1147 top_adjust,
1148 as_string(start_row));
1149 prints("'\\s0\n");
1151 prints(".\\}\n");
1154 void vertical_rule::print()
1156 printfs("\\*[" TRANSPARENT_STRING_NAME "]"
1157 ".if %1<=\\*[" QUOTE_STRING_NAME "]\\n[" LAST_PASSED_ROW_REG "] "
1158 ".nr %2 \\*[" QUOTE_STRING_NAME "]\\n[#T]\n",
1159 as_string(start_row),
1160 row_top_reg(start_row));
1161 const char *offset_table[3];
1162 if (is_double) {
1163 offset_table[0] = "-" HALF_DOUBLE_LINE_SEP;
1164 offset_table[1] = "+" HALF_DOUBLE_LINE_SEP;
1165 offset_table[2] = 0;
1167 else {
1168 offset_table[0] = "";
1169 offset_table[1] = 0;
1171 for (const char **offsetp = offset_table; *offsetp; offsetp++) {
1172 prints("\\*[" TRANSPARENT_STRING_NAME "].sp -1\n"
1173 "\\*[" TRANSPARENT_STRING_NAME "]\\v'" BODY_DEPTH);
1174 if (!bot_adjust.empty())
1175 printfs("+%1", bot_adjust);
1176 prints("'");
1177 printfs("\\h'\\n[%1]u%3'"
1178 "\\s[\\n[" LINESIZE_REG "]]"
1179 "\\D'l 0 |\\*[" QUOTE_STRING_NAME "]\\n[%2]u-1v",
1180 column_divide_reg(col),
1181 row_top_reg(start_row),
1182 *offsetp);
1183 if (!bot_adjust.empty())
1184 printfs("-(%1)", bot_adjust);
1185 // don't perform the top adjustment if the top is actually #T
1186 if (!top_adjust.empty())
1187 printfs("+((%1)*(%2>\\*[" QUOTE_STRING_NAME "]\\n["
1188 LAST_PASSED_ROW_REG "]))",
1189 top_adjust,
1190 as_string(start_row));
1191 prints("'"
1192 "\\s0\n");
1196 table::table(int nc, unsigned f, int ls, char dpc)
1197 : flags(f), nrows(0), ncolumns(nc), linesize(ls), decimal_point_char(dpc),
1198 vrule_list(0), stuff_list(0), span_list(0),
1199 entry_list(0), entry_list_tailp(&entry_list), entry(0),
1200 vline(0), row_is_all_lines(0), left_separation(0), right_separation(0),
1201 allocated_rows(0)
1203 minimum_width = new string[ncolumns];
1204 column_separation = ncolumns > 1 ? new int[ncolumns - 1] : 0;
1205 equal = new char[ncolumns];
1206 int i;
1207 for (i = 0; i < ncolumns; i++)
1208 equal[i] = 0;
1209 for (i = 0; i < ncolumns-1; i++)
1210 column_separation[i] = DEFAULT_COLUMN_SEPARATION;
1211 delim[0] = delim[1] = '\0';
1214 table::~table()
1216 for (int i = 0; i < nrows; i++) {
1217 a_delete entry[i];
1218 a_delete vline[i];
1220 a_delete entry;
1221 a_delete vline;
1222 while (entry_list) {
1223 table_entry *tem = entry_list;
1224 entry_list = entry_list->next;
1225 delete tem;
1227 ad_delete(ncolumns) minimum_width;
1228 a_delete column_separation;
1229 a_delete equal;
1230 while (stuff_list) {
1231 stuff *tem = stuff_list;
1232 stuff_list = stuff_list->next;
1233 delete tem;
1235 while (vrule_list) {
1236 vertical_rule *tem = vrule_list;
1237 vrule_list = vrule_list->next;
1238 delete tem;
1240 a_delete row_is_all_lines;
1241 while (span_list) {
1242 horizontal_span *tem = span_list;
1243 span_list = span_list->next;
1244 delete tem;
1248 void table::set_delim(char c1, char c2)
1250 delim[0] = c1;
1251 delim[1] = c2;
1254 void table::set_minimum_width(int c, const string &w)
1256 assert(c >= 0 && c < ncolumns);
1257 minimum_width[c] = w;
1260 void table::set_column_separation(int c, int n)
1262 assert(c >= 0 && c < ncolumns - 1);
1263 column_separation[c] = n;
1266 void table::set_equal_column(int c)
1268 assert(c >= 0 && c < ncolumns);
1269 equal[c] = 1;
1272 void table::add_stuff(stuff *p)
1274 stuff **pp;
1275 for (pp = &stuff_list; *pp; pp = &(*pp)->next)
1277 *pp = p;
1280 void table::add_text_line(int r, const string &s, const char *filename, int lineno)
1282 add_stuff(new text_stuff(s, r, filename, lineno));
1285 void table::add_single_hline(int r)
1287 add_stuff(new single_hline_stuff(r));
1290 void table::add_double_hline(int r)
1292 add_stuff(new double_hline_stuff(r));
1295 void table::allocate(int r)
1297 if (r >= nrows) {
1298 typedef table_entry **PPtable_entry; // work around g++ 1.36.1 bug
1299 if (r >= allocated_rows) {
1300 if (allocated_rows == 0) {
1301 allocated_rows = 16;
1302 if (allocated_rows <= r)
1303 allocated_rows = r + 1;
1304 entry = new PPtable_entry[allocated_rows];
1305 vline = new char*[allocated_rows];
1307 else {
1308 table_entry ***old_entry = entry;
1309 int old_allocated_rows = allocated_rows;
1310 allocated_rows *= 2;
1311 if (allocated_rows <= r)
1312 allocated_rows = r + 1;
1313 entry = new PPtable_entry[allocated_rows];
1314 memcpy(entry, old_entry, sizeof(table_entry**)*old_allocated_rows);
1315 a_delete old_entry;
1316 char **old_vline = vline;
1317 vline = new char*[allocated_rows];
1318 memcpy(vline, old_vline, sizeof(char*)*old_allocated_rows);
1319 a_delete old_vline;
1322 assert(allocated_rows > r);
1323 while (nrows <= r) {
1324 entry[nrows] = new table_entry*[ncolumns];
1325 int i;
1326 for (i = 0; i < ncolumns; i++)
1327 entry[nrows][i] = 0;
1328 vline[nrows] = new char[ncolumns+1];
1329 for (i = 0; i < ncolumns+1; i++)
1330 vline[nrows][i] = 0;
1331 nrows++;
1336 void table::do_hspan(int r, int c)
1338 assert(r >= 0 && c >= 0 && r < nrows && c < ncolumns);
1339 if (c == 0) {
1340 error("first column cannot be horizontally spanned");
1341 return;
1343 table_entry *e = entry[r][c];
1344 if (e) {
1345 assert(e->start_row <= r && r <= e->end_row
1346 && e->start_col <= c && c <= e->end_col
1347 && e->end_row - e->start_row > 0
1348 && e->end_col - e->start_col > 0);
1349 return;
1351 e = entry[r][c-1];
1352 // e can be 0 if we had an empty entry or an error
1353 if (e == 0)
1354 return;
1355 if (e->start_row != r) {
1358 ^ s */
1359 error("impossible horizontal span at row %1, column %2", r + 1, c + 1);
1361 else {
1362 e->end_col = c;
1363 entry[r][c] = e;
1367 void table::do_vspan(int r, int c)
1369 assert(r >= 0 && c >= 0 && r < nrows && c < ncolumns);
1370 if (r == 0) {
1371 error("first row cannot be vertically spanned");
1372 return;
1374 table_entry *e = entry[r][c];
1375 if (e) {
1376 assert(e->start_row <= r && r <= e->end_row
1377 && e->start_col <= c && c <= e->end_col
1378 && e->end_row - e->start_row > 0
1379 && e->end_col - e->start_col > 0);
1380 return;
1382 e = entry[r-1][c];
1383 // e can be 0 if we had an empty entry or an error
1384 if (e == 0)
1385 return;
1386 if (e->start_col != c) {
1387 /* l s
1388 l ^ */
1389 error("impossible vertical span at row %1, column %2", r + 1, c + 1);
1391 else {
1392 for (int i = c; i <= e->end_col; i++) {
1393 assert(entry[r][i] == 0);
1394 entry[r][i] = e;
1396 e->end_row = r;
1400 int find_decimal_point(const char *s, char decimal_point_char,
1401 const char *delim)
1403 if (s == 0 || *s == '\0')
1404 return -1;
1405 const char *p;
1406 int in_delim = 0; // is p within eqn delimiters?
1407 // tbl recognises \& even within eqn delimiters; I don't
1408 for (p = s; *p; p++)
1409 if (in_delim) {
1410 if (*p == delim[1])
1411 in_delim = 0;
1413 else if (*p == delim[0])
1414 in_delim = 1;
1415 else if (p[0] == '\\' && p[1] == '&')
1416 return p - s;
1417 int possible_pos = -1;
1418 in_delim = 0;
1419 for (p = s; *p; p++)
1420 if (in_delim) {
1421 if (*p == delim[1])
1422 in_delim = 0;
1424 else if (*p == delim[0])
1425 in_delim = 1;
1426 else if (p[0] == decimal_point_char && csdigit(p[1]))
1427 possible_pos = p - s;
1428 if (possible_pos >= 0)
1429 return possible_pos;
1430 in_delim = 0;
1431 for (p = s; *p; p++)
1432 if (in_delim) {
1433 if (*p == delim[1])
1434 in_delim = 0;
1436 else if (*p == delim[0])
1437 in_delim = 1;
1438 else if (csdigit(*p))
1439 possible_pos = p + 1 - s;
1440 return possible_pos;
1443 void table::add_entry(int r, int c, const string &str, const entry_format *f,
1444 const char *fn, int ln)
1446 allocate(r);
1447 table_entry *e = 0;
1448 if (str == "\\_") {
1449 e = new short_line_entry(f);
1451 else if (str == "\\=") {
1452 e = new short_double_line_entry(f);
1454 else if (str == "_") {
1455 single_line_entry *lefte;
1456 if (c > 0 && entry[r][c-1] != 0 &&
1457 (lefte = entry[r][c-1]->to_single_line_entry()) != 0
1458 && lefte->start_row == r
1459 && lefte->mod->stagger == f->stagger) {
1460 lefte->end_col = c;
1461 entry[r][c] = lefte;
1463 else
1464 e = new single_line_entry(f);
1466 else if (str == "=") {
1467 double_line_entry *lefte;
1468 if (c > 0 && entry[r][c-1] != 0 &&
1469 (lefte = entry[r][c-1]->to_double_line_entry()) != 0
1470 && lefte->start_row == r
1471 && lefte->mod->stagger == f->stagger) {
1472 lefte->end_col = c;
1473 entry[r][c] = lefte;
1475 else
1476 e = new double_line_entry(f);
1478 else if (str == "\\^") {
1479 do_vspan(r, c);
1481 else if (str.length() > 2 && str[0] == '\\' && str[1] == 'R') {
1482 if (str.search('\n') >= 0)
1483 error_with_file_and_line(fn, ln, "bad repeated character");
1484 else {
1485 char *s = str.substring(2, str.length() - 2).extract();
1486 e = new repeated_char_entry(s, f);
1489 else {
1490 int is_block = str.search('\n') >= 0;
1491 char *s;
1492 switch (f->type) {
1493 case FORMAT_SPAN:
1494 assert(str.empty());
1495 do_hspan(r, c);
1496 break;
1497 case FORMAT_LEFT:
1498 if (!str.empty()) {
1499 s = str.extract();
1500 if (is_block)
1501 e = new left_block_entry(s, f);
1502 else
1503 e = new left_text_entry(s, f);
1505 else
1506 e = new empty_entry(f);
1507 break;
1508 case FORMAT_CENTER:
1509 if (!str.empty()) {
1510 s = str.extract();
1511 if (is_block)
1512 e = new center_block_entry(s, f);
1513 else
1514 e = new center_text_entry(s, f);
1516 else
1517 e = new empty_entry(f);
1518 break;
1519 case FORMAT_RIGHT:
1520 if (!str.empty()) {
1521 s = str.extract();
1522 if (is_block)
1523 e = new right_block_entry(s, f);
1524 else
1525 e = new right_text_entry(s, f);
1527 else
1528 e = new empty_entry(f);
1529 break;
1530 case FORMAT_NUMERIC:
1531 if (!str.empty()) {
1532 s = str.extract();
1533 if (is_block) {
1534 error_with_file_and_line(fn, ln, "can't have numeric text block");
1535 e = new left_block_entry(s, f);
1537 else {
1538 int pos = find_decimal_point(s, decimal_point_char, delim);
1539 if (pos < 0)
1540 e = new center_text_entry(s, f);
1541 else
1542 e = new numeric_text_entry(s, f, pos);
1545 else
1546 e = new empty_entry(f);
1547 break;
1548 case FORMAT_ALPHABETIC:
1549 if (!str.empty()) {
1550 s = str.extract();
1551 if (is_block)
1552 e = new alphabetic_block_entry(s, f);
1553 else
1554 e = new alphabetic_text_entry(s, f);
1556 else
1557 e = new empty_entry(f);
1558 break;
1559 case FORMAT_VSPAN:
1560 do_vspan(r, c);
1561 break;
1562 case FORMAT_HLINE:
1563 if (str.length() != 0)
1564 error_with_file_and_line(fn, ln,
1565 "non-empty data entry for `_' format ignored");
1566 e = new single_line_entry(f);
1567 break;
1568 case FORMAT_DOUBLE_HLINE:
1569 if (str.length() != 0)
1570 error_with_file_and_line(fn, ln,
1571 "non-empty data entry for `=' format ignored");
1572 e = new double_line_entry(f);
1573 break;
1574 default:
1575 assert(0);
1578 if (e) {
1579 table_entry *preve = entry[r][c];
1580 if (preve) {
1581 /* c s
1582 ^ l */
1583 error_with_file_and_line(fn, ln, "row %1, column %2 already spanned",
1584 r + 1, c + 1);
1585 delete e;
1587 else {
1588 e->input_lineno = ln;
1589 e->input_filename = fn;
1590 e->start_row = e->end_row = r;
1591 e->start_col = e->end_col = c;
1592 *entry_list_tailp = e;
1593 entry_list_tailp = &e->next;
1594 entry[r][c] = e;
1599 // add vertical lines for row r
1601 void table::add_vlines(int r, const char *v)
1603 allocate(r);
1604 for (int i = 0; i < ncolumns+1; i++)
1605 vline[r][i] = v[i];
1608 void table::check()
1610 table_entry *p = entry_list;
1611 int i, j;
1612 while (p) {
1613 for (i = p->start_row; i <= p->end_row; i++)
1614 for (j = p->start_col; j <= p->end_col; j++)
1615 assert(entry[i][j] == p);
1616 p = p->next;
1620 void table::print()
1622 location_force_filename = 1;
1623 check();
1624 init_output();
1625 determine_row_type();
1626 compute_widths();
1627 if (!(flags & CENTER))
1628 prints(".if \\n[" SAVED_CENTER_REG "] \\{");
1629 prints(".in +(u;\\n[.l]-\\n[.i]-\\n[TW]/2>?-\\n[.i])\n"
1630 ".nr " SAVED_INDENT_REG " \\n[.i]\n");
1631 if (!(flags & CENTER))
1632 prints(".\\}\n");
1633 build_vrule_list();
1634 define_bottom_macro();
1635 do_top();
1636 for (int i = 0; i < nrows; i++)
1637 do_row(i);
1638 do_bottom();
1641 void table::determine_row_type()
1643 row_is_all_lines = new char[nrows];
1644 for (int i = 0; i < nrows; i++) {
1645 int had_single = 0;
1646 int had_double = 0;
1647 int had_non_line = 0;
1648 for (int c = 0; c < ncolumns; c++) {
1649 table_entry *e = entry[i][c];
1650 if (e != 0) {
1651 if (e->start_row == e->end_row) {
1652 int t = e->line_type();
1653 switch (t) {
1654 case -1:
1655 had_non_line = 1;
1656 break;
1657 case 0:
1658 // empty
1659 break;
1660 case 1:
1661 had_single = 1;
1662 break;
1663 case 2:
1664 had_double = 1;
1665 break;
1666 default:
1667 assert(0);
1669 if (had_non_line)
1670 break;
1672 c = e->end_col;
1675 if (had_non_line)
1676 row_is_all_lines[i] = 0;
1677 else if (had_double)
1678 row_is_all_lines[i] = 2;
1679 else if (had_single)
1680 row_is_all_lines[i] = 1;
1681 else
1682 row_is_all_lines[i] = 0;
1686 void table::init_output()
1688 prints(".nr " COMPATIBLE_REG " \\n(.C\n"
1689 ".cp 0\n");
1690 if (linesize > 0)
1691 printfs(".nr " LINESIZE_REG " %1\n", as_string(linesize));
1692 else
1693 prints(".nr " LINESIZE_REG " \\n[.s]\n");
1694 if (!(flags & CENTER))
1695 prints(".nr " SAVED_CENTER_REG " \\n[.ce]\n");
1696 prints(".de " RESET_MACRO_NAME "\n"
1697 ".ft \\n[.f]\n"
1698 ".ps \\n[.s]\n"
1699 ".vs \\n[.v]u\n"
1700 ".in \\n[.i]u\n"
1701 ".ll \\n[.l]u\n"
1702 ".ls \\n[.L]\n"
1703 ".ad \\n[.j]\n"
1704 ".ie \\n[.u] .fi\n"
1705 ".el .nf\n"
1706 ".ce \\n[.ce]\n"
1707 "..\n"
1708 ".nr " SAVED_INDENT_REG " \\n[.i]\n"
1709 ".nr " SAVED_FONT_REG " \\n[.f]\n"
1710 ".nr " SAVED_SIZE_REG " \\n[.s]\n"
1711 ".nr " SAVED_FILL_REG " \\n[.u]\n"
1712 ".nr T. 0\n"
1713 ".nr " CURRENT_ROW_REG " 0-1\n"
1714 ".nr " LAST_PASSED_ROW_REG " 0-1\n"
1715 ".nr " SECTION_DIVERSION_FLAG_REG " 0\n"
1716 ".ds " TRANSPARENT_STRING_NAME "\n"
1717 ".ds " QUOTE_STRING_NAME "\n"
1718 ".nr " NEED_BOTTOM_RULE_REG " 1\n"
1719 ".nr " SUPPRESS_BOTTOM_REG " 0\n"
1720 ".eo\n"
1721 ".de " REPEATED_MARK_MACRO "\n"
1722 ".mk \\$1\n"
1723 ".if !'\\n(.z'' \\!." REPEATED_MARK_MACRO " \"\\$1\"\n"
1724 "..\n"
1725 ".de " REPEATED_VPT_MACRO "\n"
1726 ".vpt \\$1\n"
1727 ".if !'\\n(.z'' \\!." REPEATED_VPT_MACRO " \"\\$1\"\n"
1728 "..\n");
1729 if (!(flags & NOKEEP))
1730 prints(".de " KEEP_MACRO_NAME "\n"
1731 ".if '\\n[.z]'' \\{.ds " QUOTE_STRING_NAME " \\\\\n"
1732 ".ds " TRANSPARENT_STRING_NAME " \\!\n"
1733 ".di " SECTION_DIVERSION_NAME "\n"
1734 ".nr " SECTION_DIVERSION_FLAG_REG " 1\n"
1735 ".in 0\n"
1736 ".\\}\n"
1737 "..\n"
1738 ".de " RELEASE_MACRO_NAME "\n"
1739 ".if \\n[" SECTION_DIVERSION_FLAG_REG "] \\{"
1740 ".di\n"
1741 ".in \\n[" SAVED_INDENT_REG "]u\n"
1742 ".nr " SAVED_DN_REG " \\n[dn]\n"
1743 ".ds " QUOTE_STRING_NAME "\n"
1744 ".ds " TRANSPARENT_STRING_NAME "\n"
1745 ".nr " SECTION_DIVERSION_FLAG_REG " 0\n"
1746 ".if \\n[.t]<=\\n[dn] \\{"
1747 ".nr T. 1\n"
1748 ".T#\n"
1749 ".nr " SUPPRESS_BOTTOM_REG " 1\n"
1750 ".sp \\n[.t]u\n"
1751 ".nr " SUPPRESS_BOTTOM_REG " 0\n"
1752 ".mk #T\n"
1753 ".\\}\n"
1754 ".if \\n[.t]<=\\n[" SAVED_DN_REG "] "
1755 /* Since we turn off traps, it won't get into an infinite loop
1756 when we try and print it; it will just go off the bottom of the
1757 page. */
1758 ".tm warning: page \\n%: table text block will not fit on one page\n"
1759 ".nf\n"
1760 ".ls 1\n"
1761 "." SECTION_DIVERSION_NAME "\n"
1762 ".ls\n"
1763 ".rm " SECTION_DIVERSION_NAME "\n"
1764 ".\\}\n"
1765 "..\n"
1766 ".nr " TABLE_DIVERSION_FLAG_REG " 0\n"
1767 ".de " TABLE_KEEP_MACRO_NAME "\n"
1768 ".if '\\n[.z]'' \\{"
1769 ".di " TABLE_DIVERSION_NAME "\n"
1770 ".nr " TABLE_DIVERSION_FLAG_REG " 1\n"
1771 ".\\}\n"
1772 "..\n"
1773 ".de " TABLE_RELEASE_MACRO_NAME "\n"
1774 ".if \\n[" TABLE_DIVERSION_FLAG_REG "] \\{.br\n"
1775 ".di\n"
1776 ".nr " SAVED_DN_REG " \\n[dn]\n"
1777 ".ne \\n[dn]u+\\n[.V]u\n"
1778 ".ie \\n[.t]<=\\n[" SAVED_DN_REG "] "
1779 ".tm error: page \\n%: table will not fit on one page; use .TS H/.TH with a supporting macro package\n"
1780 ".el \\{"
1781 ".in 0\n"
1782 ".ls 1\n"
1783 ".nf\n"
1784 "." TABLE_DIVERSION_NAME "\n"
1785 ".\\}\n"
1786 ".rm " TABLE_DIVERSION_NAME "\n"
1787 ".\\}\n"
1788 "..\n");
1789 prints(".ec\n"
1790 ".ce 0\n"
1791 ".nf\n");
1794 string block_width_reg(int r, int c)
1796 static char name[sizeof(BLOCK_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1797 sprintf(name, BLOCK_WIDTH_PREFIX "%d,%d", r, c);
1798 return string(name);
1801 string block_diversion_name(int r, int c)
1803 static char name[sizeof(BLOCK_DIVERSION_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1804 sprintf(name, BLOCK_DIVERSION_PREFIX "%d,%d", r, c);
1805 return string(name);
1808 string block_height_reg(int r, int c)
1810 static char name[sizeof(BLOCK_HEIGHT_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1811 sprintf(name, BLOCK_HEIGHT_PREFIX "%d,%d", r, c);
1812 return string(name);
1815 string span_width_reg(int start_col, int end_col)
1817 static char name[sizeof(SPAN_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1818 sprintf(name, SPAN_WIDTH_PREFIX "%d", start_col);
1819 if (end_col != start_col)
1820 sprintf(strchr(name, '\0'), ",%d", end_col);
1821 return string(name);
1824 string span_left_numeric_width_reg(int start_col, int end_col)
1826 static char name[sizeof(SPAN_LEFT_NUMERIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1827 sprintf(name, SPAN_LEFT_NUMERIC_WIDTH_PREFIX "%d", start_col);
1828 if (end_col != start_col)
1829 sprintf(strchr(name, '\0'), ",%d", end_col);
1830 return string(name);
1833 string span_right_numeric_width_reg(int start_col, int end_col)
1835 static char name[sizeof(SPAN_RIGHT_NUMERIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1836 sprintf(name, SPAN_RIGHT_NUMERIC_WIDTH_PREFIX "%d", start_col);
1837 if (end_col != start_col)
1838 sprintf(strchr(name, '\0'), ",%d", end_col);
1839 return string(name);
1842 string span_alphabetic_width_reg(int start_col, int end_col)
1844 static char name[sizeof(SPAN_ALPHABETIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1845 sprintf(name, SPAN_ALPHABETIC_WIDTH_PREFIX "%d", start_col);
1846 if (end_col != start_col)
1847 sprintf(strchr(name, '\0'), ",%d", end_col);
1848 return string(name);
1851 string column_separation_reg(int col)
1853 static char name[sizeof(COLUMN_SEPARATION_PREFIX)+INT_DIGITS];
1854 sprintf(name, COLUMN_SEPARATION_PREFIX "%d", col);
1855 return string(name);
1858 string row_start_reg(int row)
1860 static char name[sizeof(ROW_START_PREFIX)+INT_DIGITS];
1861 sprintf(name, ROW_START_PREFIX "%d", row);
1862 return string(name);
1865 string column_start_reg(int col)
1867 static char name[sizeof(COLUMN_START_PREFIX)+INT_DIGITS];
1868 sprintf(name, COLUMN_START_PREFIX "%d", col);
1869 return string(name);
1872 string column_end_reg(int col)
1874 static char name[sizeof(COLUMN_END_PREFIX)+INT_DIGITS];
1875 sprintf(name, COLUMN_END_PREFIX "%d", col);
1876 return string(name);
1879 string column_divide_reg(int col)
1881 static char name[sizeof(COLUMN_DIVIDE_PREFIX)+INT_DIGITS];
1882 sprintf(name, COLUMN_DIVIDE_PREFIX "%d", col);
1883 return string(name);
1886 string row_top_reg(int row)
1888 static char name[sizeof(ROW_TOP_PREFIX)+INT_DIGITS];
1889 sprintf(name, ROW_TOP_PREFIX "%d", row);
1890 return string(name);
1893 void init_span_reg(int start_col, int end_col)
1895 printfs(".nr %1 \\n(.H\n.nr %2 0\n.nr %3 0\n.nr %4 0\n",
1896 span_width_reg(start_col, end_col),
1897 span_alphabetic_width_reg(start_col, end_col),
1898 span_left_numeric_width_reg(start_col, end_col),
1899 span_right_numeric_width_reg(start_col, end_col));
1902 void compute_span_width(int start_col, int end_col)
1904 printfs(".nr %1 \\n[%1]>?(\\n[%2]+\\n[%3])\n"
1905 ".if \\n[%4] .nr %1 \\n[%1]>?(\\n[%4]+2n)\n",
1906 span_width_reg(start_col, end_col),
1907 span_left_numeric_width_reg(start_col, end_col),
1908 span_right_numeric_width_reg(start_col, end_col),
1909 span_alphabetic_width_reg(start_col, end_col));
1912 // Increase the widths of columns so that the width of any spanning entry
1913 // is not greater than the sum of the widths of the columns that it spans.
1914 // Ensure that the widths of columns remain equal.
1916 void table::divide_span(int start_col, int end_col)
1918 assert(end_col > start_col);
1919 printfs(".nr " NEEDED_REG " \\n[%1]-(\\n[%2]",
1920 span_width_reg(start_col, end_col),
1921 span_width_reg(start_col, start_col));
1922 int i;
1923 for (i = start_col + 1; i <= end_col; i++) {
1924 // The column separation may shrink with the expand option.
1925 if (!(flags & EXPAND))
1926 printfs("+%1n", as_string(column_separation[i - 1]));
1927 printfs("+\\n[%1]", span_width_reg(i, i));
1929 prints(")\n");
1930 printfs(".nr " NEEDED_REG " \\n[" NEEDED_REG "]/%1\n",
1931 as_string(end_col - start_col + 1));
1932 prints(".if \\n[" NEEDED_REG "] \\{");
1933 for (i = start_col; i <= end_col; i++)
1934 printfs(".nr %1 +\\n[" NEEDED_REG "]\n",
1935 span_width_reg(i, i));
1936 int equal_flag = 0;
1937 for (i = start_col; i <= end_col && !equal_flag; i++)
1938 if (equal[i])
1939 equal_flag = 1;
1940 if (equal_flag) {
1941 for (i = 0; i < ncolumns; i++)
1942 if (i < start_col || i > end_col)
1943 printfs(".nr %1 +\\n[" NEEDED_REG "]\n",
1944 span_width_reg(i, i));
1946 prints(".\\}\n");
1949 void table::sum_columns(int start_col, int end_col)
1951 assert(end_col > start_col);
1952 printfs(".nr %1 \\n[%2]",
1953 span_width_reg(start_col, end_col),
1954 span_width_reg(start_col, start_col));
1955 for (int i = start_col + 1; i <= end_col; i++)
1956 printfs("+(%1*\\n[" SEPARATION_FACTOR_REG "])+\\n[%2]",
1957 as_string(column_separation[i - 1]),
1958 span_width_reg(i, i));
1959 prints('\n');
1962 horizontal_span::horizontal_span(int sc, int ec, horizontal_span *p)
1963 : next(p), start_col(sc), end_col(ec)
1967 void table::build_span_list()
1969 span_list = 0;
1970 table_entry *p = entry_list;
1971 while (p) {
1972 if (p->end_col != p->start_col) {
1973 horizontal_span *q;
1974 for (q = span_list; q; q = q->next)
1975 if (q->start_col == p->start_col
1976 && q->end_col == p->end_col)
1977 break;
1978 if (!q)
1979 span_list = new horizontal_span(p->start_col, p->end_col, span_list);
1981 p = p->next;
1983 // Now sort span_list primarily by order of end_row, and secondarily
1984 // by reverse order of start_row. This ensures that if we divide
1985 // spans using the order in span_list, we will get reasonable results.
1986 horizontal_span *unsorted = span_list;
1987 span_list = 0;
1988 while (unsorted) {
1989 horizontal_span **pp;
1990 for (pp = &span_list; *pp; pp = &(*pp)->next)
1991 if (unsorted->end_col < (*pp)->end_col
1992 || (unsorted->end_col == (*pp)->end_col
1993 && (unsorted->start_col > (*pp)->start_col)))
1994 break;
1995 horizontal_span *tem = unsorted->next;
1996 unsorted->next = *pp;
1997 *pp = unsorted;
1998 unsorted = tem;
2002 void table::compute_separation_factor()
2004 if (flags & (ALLBOX|BOX|DOUBLEBOX))
2005 left_separation = right_separation = 1;
2006 else {
2007 for (int i = 0; i < nrows; i++) {
2008 if (vline[i][0] > 0)
2009 left_separation = 1;
2010 if (vline[i][ncolumns] > 0)
2011 right_separation = 1;
2014 if (flags & EXPAND) {
2015 int total_sep = left_separation + right_separation;
2016 int i;
2017 for (i = 0; i < ncolumns - 1; i++)
2018 total_sep += column_separation[i];
2019 if (total_sep != 0) {
2020 // Don't let the separation factor be negative.
2021 prints(".nr " SEPARATION_FACTOR_REG " \\n[.l]-\\n[.i]");
2022 for (i = 0; i < ncolumns; i++)
2023 printfs("-\\n[%1]", span_width_reg(i, i));
2024 printfs("/%1>?0\n", as_string(total_sep));
2029 void table::compute_column_positions()
2031 printfs(".nr %1 0\n", column_divide_reg(0));
2032 printfs(".nr %1 %2*\\n[" SEPARATION_FACTOR_REG "]\n",
2033 column_start_reg(0),
2034 as_string(left_separation));
2035 int i;
2036 for (i = 1;; i++) {
2037 printfs(".nr %1 \\n[%2]+\\n[%3]\n",
2038 column_end_reg(i-1),
2039 column_start_reg(i-1),
2040 span_width_reg(i-1, i-1));
2041 if (i >= ncolumns)
2042 break;
2043 printfs(".nr %1 \\n[%2]+(%3*\\n[" SEPARATION_FACTOR_REG "])\n",
2044 column_start_reg(i),
2045 column_end_reg(i-1),
2046 as_string(column_separation[i-1]));
2047 printfs(".nr %1 \\n[%2]+\\n[%3]/2\n",
2048 column_divide_reg(i),
2049 column_end_reg(i-1),
2050 column_start_reg(i));
2052 printfs(".nr %1 \\n[%2]+(%3*\\n[" SEPARATION_FACTOR_REG "])\n",
2053 column_divide_reg(ncolumns),
2054 column_end_reg(i-1),
2055 as_string(right_separation));
2056 printfs(".nr TW \\n[%1]\n",
2057 column_divide_reg(ncolumns));
2058 if (flags & DOUBLEBOX) {
2059 printfs(".nr %1 +" DOUBLE_LINE_SEP "\n", column_divide_reg(0));
2060 printfs(".nr %1 -" DOUBLE_LINE_SEP "\n", column_divide_reg(ncolumns));
2064 void table::make_columns_equal()
2066 int first = -1; // index of first equal column
2067 int i;
2068 for (i = 0; i < ncolumns; i++)
2069 if (equal[i]) {
2070 if (first < 0) {
2071 printfs(".nr %1 \\n[%1]", span_width_reg(i, i));
2072 first = i;
2074 else
2075 printfs(">?\\n[%1]", span_width_reg(i, i));
2077 if (first >= 0) {
2078 prints('\n');
2079 for (i = first + 1; i < ncolumns; i++)
2080 if (equal[i])
2081 printfs(".nr %1 \\n[%2]\n",
2082 span_width_reg(i, i),
2083 span_width_reg(first, first));
2087 void table::compute_widths()
2089 build_span_list();
2090 int i;
2091 horizontal_span *p;
2092 prints(".nr " SEPARATION_FACTOR_REG " 1n\n");
2093 for (i = 0; i < ncolumns; i++) {
2094 init_span_reg(i, i);
2095 if (!minimum_width[i].empty())
2096 printfs(".nr %1 %2\n", span_width_reg(i, i), minimum_width[i]);
2098 for (p = span_list; p; p = p->next)
2099 init_span_reg(p->start_col, p->end_col);
2100 table_entry *q;
2101 for (q = entry_list; q; q = q->next)
2102 if (!q->mod->zero_width)
2103 q->do_width();
2104 for (i = 0; i < ncolumns; i++)
2105 compute_span_width(i, i);
2106 for (p = span_list; p; p = p->next)
2107 compute_span_width(p->start_col, p->end_col);
2108 make_columns_equal();
2109 // Note that divide_span keeps equal width columns equal.
2110 for (p = span_list; p; p = p->next)
2111 divide_span(p->start_col, p->end_col);
2112 for (p = span_list; p; p = p->next)
2113 sum_columns(p->start_col, p->end_col);
2114 int had_spanning_block = 0;
2115 int had_equal_block = 0;
2116 for (q = entry_list; q; q = q->next)
2117 if (q->divert(ncolumns, minimum_width,
2118 (flags & EXPAND) ? column_separation : 0)) {
2119 if (q->end_col > q->start_col)
2120 had_spanning_block = 1;
2121 for (i = q->start_col; i <= q->end_col && !had_equal_block; i++)
2122 if (equal[i])
2123 had_equal_block = 1;
2125 if (had_equal_block)
2126 make_columns_equal();
2127 if (had_spanning_block)
2128 for (p = span_list; p; p = p->next)
2129 divide_span(p->start_col, p->end_col);
2130 compute_separation_factor();
2131 for (p = span_list; p; p = p->next)
2132 sum_columns(p->start_col, p->end_col);
2133 compute_column_positions();
2136 void table::print_single_hline(int r)
2138 prints(".vs " LINE_SEP ">?\\n[.V]u\n"
2139 ".ls 1\n"
2140 "\\v'" BODY_DEPTH "'"
2141 "\\s[\\n[" LINESIZE_REG "]]");
2142 if (r > nrows - 1)
2143 prints("\\D'l |\\n[TW]u 0'");
2144 else {
2145 int start_col = 0;
2146 for (;;) {
2147 while (start_col < ncolumns
2148 && entry[r][start_col] != 0
2149 && entry[r][start_col]->start_row != r)
2150 start_col++;
2151 int end_col;
2152 for (end_col = start_col;
2153 end_col < ncolumns
2154 && (entry[r][end_col] == 0
2155 || entry[r][end_col]->start_row == r);
2156 end_col++)
2158 if (end_col <= start_col)
2159 break;
2160 printfs("\\h'|\\n[%1]u",
2161 column_divide_reg(start_col));
2162 if ((r > 0 && vline[r-1][start_col] == 2)
2163 || (r < nrows && vline[r][start_col] == 2))
2164 prints("-" HALF_DOUBLE_LINE_SEP);
2165 prints("'");
2166 printfs("\\D'l |\\n[%1]u",
2167 column_divide_reg(end_col));
2168 if ((r > 0 && vline[r-1][end_col] == 2)
2169 || (r < nrows && vline[r][end_col] == 2))
2170 prints("+" HALF_DOUBLE_LINE_SEP);
2171 prints(" 0'");
2172 start_col = end_col;
2175 prints("\\s0\n");
2176 prints(".ls\n"
2177 ".vs\n");
2180 void table::print_double_hline(int r)
2182 prints(".vs " LINE_SEP "+" DOUBLE_LINE_SEP
2183 ">?\\n[.V]u\n"
2184 ".ls 1\n"
2185 "\\v'" BODY_DEPTH "'"
2186 "\\s[\\n[" LINESIZE_REG "]]");
2187 if (r > nrows - 1)
2188 prints("\\v'-" DOUBLE_LINE_SEP "'"
2189 "\\D'l |\\n[TW]u 0'"
2190 "\\v'" DOUBLE_LINE_SEP "'"
2191 "\\h'|0'"
2192 "\\D'l |\\n[TW]u 0'");
2193 else {
2194 int start_col = 0;
2195 for (;;) {
2196 while (start_col < ncolumns
2197 && entry[r][start_col] != 0
2198 && entry[r][start_col]->start_row != r)
2199 start_col++;
2200 int end_col;
2201 for (end_col = start_col;
2202 end_col < ncolumns
2203 && (entry[r][end_col] == 0
2204 || entry[r][end_col]->start_row == r);
2205 end_col++)
2207 if (end_col <= start_col)
2208 break;
2209 const char *left_adjust = 0;
2210 if ((r > 0 && vline[r-1][start_col] == 2)
2211 || (r < nrows && vline[r][start_col] == 2))
2212 left_adjust = "-" HALF_DOUBLE_LINE_SEP;
2213 const char *right_adjust = 0;
2214 if ((r > 0 && vline[r-1][end_col] == 2)
2215 || (r < nrows && vline[r][end_col] == 2))
2216 right_adjust = "+" HALF_DOUBLE_LINE_SEP;
2217 printfs("\\v'-" DOUBLE_LINE_SEP "'"
2218 "\\h'|\\n[%1]u",
2219 column_divide_reg(start_col));
2220 if (left_adjust)
2221 prints(left_adjust);
2222 prints("'");
2223 printfs("\\D'l |\\n[%1]u",
2224 column_divide_reg(end_col));
2225 if (right_adjust)
2226 prints(right_adjust);
2227 prints(" 0'");
2228 printfs("\\v'" DOUBLE_LINE_SEP "'"
2229 "\\h'|\\n[%1]u",
2230 column_divide_reg(start_col));
2231 if (left_adjust)
2232 prints(left_adjust);
2233 prints("'");
2234 printfs("\\D'l |\\n[%1]u",
2235 column_divide_reg(end_col));
2236 if (right_adjust)
2237 prints(right_adjust);
2238 prints(" 0'");
2239 start_col = end_col;
2242 prints("\\s0\n"
2243 ".ls\n"
2244 ".vs\n");
2247 void table::compute_vrule_top_adjust(int start_row, int col, string &result)
2249 if (row_is_all_lines[start_row] && start_row < nrows - 1) {
2250 if (row_is_all_lines[start_row] == 2)
2251 result = LINE_SEP ">?\\n[.V]u" "+" DOUBLE_LINE_SEP;
2252 else
2253 result = LINE_SEP ">?\\n[.V]u";
2254 start_row++;
2256 else {
2257 result = "";
2258 if (start_row == 0)
2259 return;
2260 for (stuff *p = stuff_list; p && p->row <= start_row; p = p->next)
2261 if (p->row == start_row
2262 && (p->is_single_line() || p->is_double_line()))
2263 return;
2265 int left = 0;
2266 if (col > 0) {
2267 table_entry *e = entry[start_row-1][col-1];
2268 if (e && e->start_row == e->end_row) {
2269 if (e->to_double_line_entry() != 0)
2270 left = 2;
2271 else if (e->to_single_line_entry() != 0)
2272 left = 1;
2275 int right = 0;
2276 if (col < ncolumns) {
2277 table_entry *e = entry[start_row-1][col];
2278 if (e && e->start_row == e->end_row) {
2279 if (e->to_double_line_entry() != 0)
2280 right = 2;
2281 else if (e->to_single_line_entry() != 0)
2282 right = 1;
2285 if (row_is_all_lines[start_row-1] == 0) {
2286 if (left > 0 || right > 0) {
2287 result += "-" BODY_DEPTH "-" BAR_HEIGHT;
2288 if ((left == 2 && right != 2) || (right == 2 && left != 2))
2289 result += "-" HALF_DOUBLE_LINE_SEP;
2290 else if (left == 2 && right == 2)
2291 result += "+" HALF_DOUBLE_LINE_SEP;
2294 else if (row_is_all_lines[start_row-1] == 2) {
2295 if ((left == 2 && right != 2) || (right == 2 && left != 2))
2296 result += "-" DOUBLE_LINE_SEP;
2297 else if (left == 1 || right == 1)
2298 result += "-" HALF_DOUBLE_LINE_SEP;
2302 void table::compute_vrule_bot_adjust(int end_row, int col, string &result)
2304 if (row_is_all_lines[end_row] && end_row > 0) {
2305 end_row--;
2306 result = "";
2308 else {
2309 stuff *p;
2310 for (p = stuff_list; p && p->row < end_row + 1; p = p->next)
2312 if (p && p->row == end_row + 1 && p->is_double_line()) {
2313 result = "-" DOUBLE_LINE_SEP;
2314 return;
2316 if ((p != 0 && p->row == end_row + 1)
2317 || end_row == nrows - 1) {
2318 result = "";
2319 return;
2321 if (row_is_all_lines[end_row+1] == 1)
2322 result = LINE_SEP;
2323 else if (row_is_all_lines[end_row+1] == 2)
2324 result = LINE_SEP "+" DOUBLE_LINE_SEP;
2325 else
2326 result = "";
2328 int left = 0;
2329 if (col > 0) {
2330 table_entry *e = entry[end_row+1][col-1];
2331 if (e && e->start_row == e->end_row) {
2332 if (e->to_double_line_entry() != 0)
2333 left = 2;
2334 else if (e->to_single_line_entry() != 0)
2335 left = 1;
2338 int right = 0;
2339 if (col < ncolumns) {
2340 table_entry *e = entry[end_row+1][col];
2341 if (e && e->start_row == e->end_row) {
2342 if (e->to_double_line_entry() != 0)
2343 right = 2;
2344 else if (e->to_single_line_entry() != 0)
2345 right = 1;
2348 if (row_is_all_lines[end_row+1] == 0) {
2349 if (left > 0 || right > 0) {
2350 result = "1v-" BODY_DEPTH "-" BAR_HEIGHT;
2351 if ((left == 2 && right != 2) || (right == 2 && left != 2))
2352 result += "+" HALF_DOUBLE_LINE_SEP;
2353 else if (left == 2 && right == 2)
2354 result += "-" HALF_DOUBLE_LINE_SEP;
2357 else if (row_is_all_lines[end_row+1] == 2) {
2358 if (left == 2 && right == 2)
2359 result += "-" DOUBLE_LINE_SEP;
2360 else if (left != 2 && right != 2 && (left == 1 || right == 1))
2361 result += "-" HALF_DOUBLE_LINE_SEP;
2365 void table::add_vertical_rule(int start_row, int end_row, int col, int is_double)
2367 vrule_list = new vertical_rule(start_row, end_row, col, is_double,
2368 vrule_list);
2369 compute_vrule_top_adjust(start_row, col, vrule_list->top_adjust);
2370 compute_vrule_bot_adjust(end_row, col, vrule_list->bot_adjust);
2373 void table::build_vrule_list()
2375 int col;
2376 if (flags & ALLBOX) {
2377 for (col = 1; col < ncolumns; col++) {
2378 int start_row = 0;
2379 for (;;) {
2380 while (start_row < nrows && vline_spanned(start_row, col))
2381 start_row++;
2382 if (start_row >= nrows)
2383 break;
2384 int end_row = start_row;
2385 while (end_row < nrows && !vline_spanned(end_row, col))
2386 end_row++;
2387 end_row--;
2388 add_vertical_rule(start_row, end_row, col, 0);
2389 start_row = end_row + 1;
2393 if (flags & (BOX|ALLBOX|DOUBLEBOX)) {
2394 add_vertical_rule(0, nrows - 1, 0, 0);
2395 add_vertical_rule(0, nrows - 1, ncolumns, 0);
2397 for (int end_row = 0; end_row < nrows; end_row++)
2398 for (col = 0; col < ncolumns+1; col++)
2399 if (vline[end_row][col] > 0
2400 && !vline_spanned(end_row, col)
2401 && (end_row == nrows - 1
2402 || vline[end_row+1][col] != vline[end_row][col]
2403 || vline_spanned(end_row+1, col))) {
2404 int start_row;
2405 for (start_row = end_row - 1;
2406 start_row >= 0
2407 && vline[start_row][col] == vline[end_row][col]
2408 && !vline_spanned(start_row, col);
2409 start_row--)
2411 start_row++;
2412 add_vertical_rule(start_row, end_row, col, vline[end_row][col] > 1);
2414 for (vertical_rule *p = vrule_list; p; p = p->next)
2415 if (p->is_double)
2416 for (int r = p->start_row; r <= p->end_row; r++) {
2417 if (p->col > 0 && entry[r][p->col-1] != 0
2418 && entry[r][p->col-1]->end_col == p->col-1) {
2419 int is_corner = r == p->start_row || r == p->end_row;
2420 entry[r][p->col-1]->note_double_vrule_on_right(is_corner);
2422 if (p->col < ncolumns && entry[r][p->col] != 0
2423 && entry[r][p->col]->start_col == p->col) {
2424 int is_corner = r == p->start_row || r == p->end_row;
2425 entry[r][p->col]->note_double_vrule_on_left(is_corner);
2430 void table::define_bottom_macro()
2432 prints(".eo\n"
2433 ".de T#\n"
2434 ".if !\\n[" SUPPRESS_BOTTOM_REG "] \\{"
2435 "." REPEATED_VPT_MACRO " 0\n"
2436 ".mk " SAVED_VERTICAL_POS_REG "\n");
2437 if (flags & (BOX|ALLBOX|DOUBLEBOX)) {
2438 prints(".if \\n[T.]&\\n[" NEED_BOTTOM_RULE_REG "] \\{");
2439 print_single_hline(0);
2440 prints(".\\}\n");
2442 prints(".ls 1\n");
2443 for (vertical_rule *p = vrule_list; p; p = p->next)
2444 p->contribute_to_bottom_macro(this);
2445 if (flags & DOUBLEBOX)
2446 prints(".if \\n[T.] \\{.vs " DOUBLE_LINE_SEP ">?\\n[.V]u\n"
2447 "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]"
2448 "\\D'l \\n[TW]u 0'\\s0\n"
2449 ".vs\n"
2450 ".\\}\n"
2451 ".if \\n[" LAST_PASSED_ROW_REG "]>=0 "
2452 ".nr " TOP_REG " \\n[#T]-" DOUBLE_LINE_SEP "\n"
2453 ".sp -1\n"
2454 "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]"
2455 "\\D'l 0 |\\n[" TOP_REG "]u-1v'\\s0\n"
2456 ".sp -1\n"
2457 "\\v'" BODY_DEPTH "'\\h'|\\n[TW]u'\\s[\\n[" LINESIZE_REG "]]"
2458 "\\D'l 0 |\\n[" TOP_REG "]u-1v'\\s0\n");
2459 prints(".ls\n");
2460 prints(".nr " LAST_PASSED_ROW_REG " \\n[" CURRENT_ROW_REG "]\n"
2461 ".sp |\\n[" SAVED_VERTICAL_POS_REG "]u\n"
2462 "." REPEATED_VPT_MACRO " 1\n"
2463 ".\\}\n"
2464 "..\n"
2465 ".ec\n");
2468 // is the vertical line before column c in row r horizontally spanned?
2470 int table::vline_spanned(int r, int c)
2472 assert(r >= 0 && r < nrows && c >= 0 && c < ncolumns + 1);
2473 return (c != 0 && c != ncolumns && entry[r][c] != 0
2474 && entry[r][c]->start_col != c
2475 // horizontally spanning lines don't count
2476 && entry[r][c]->to_double_line_entry() == 0
2477 && entry[r][c]->to_single_line_entry() == 0);
2480 int table::row_begins_section(int r)
2482 assert(r >= 0 && r < nrows);
2483 for (int i = 0; i < ncolumns; i++)
2484 if (entry[r][i] && entry[r][i]->start_row != r)
2485 return 0;
2486 return 1;
2489 int table::row_ends_section(int r)
2491 assert(r >= 0 && r < nrows);
2492 for (int i = 0; i < ncolumns; i++)
2493 if (entry[r][i] && entry[r][i]->end_row != r)
2494 return 0;
2495 return 1;
2498 void table::do_row(int r)
2500 if (!(flags & NOKEEP) && row_begins_section(r))
2501 prints("." KEEP_MACRO_NAME "\n");
2502 int had_line = 0;
2503 stuff *p;
2504 for (p = stuff_list; p && p->row < r; p = p->next)
2506 for (stuff *p1 = p; p1 && p1->row == r; p1 = p1->next)
2507 if (!p1->printed && (p1->is_single_line() || p1->is_double_line())) {
2508 had_line = 1;
2509 break;
2511 if (!had_line && !row_is_all_lines[r])
2512 printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r));
2513 had_line = 0;
2514 for (; p && p->row == r; p = p->next)
2515 if (!p->printed) {
2516 p->print(this);
2517 if (!had_line && (p->is_single_line() || p->is_double_line())) {
2518 printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r));
2519 had_line = 1;
2522 // Change the row *after* printing the stuff list (which might contain .TH).
2523 printfs("\\*[" TRANSPARENT_STRING_NAME "].nr " CURRENT_ROW_REG " %1\n",
2524 as_string(r));
2525 if (!had_line && row_is_all_lines[r])
2526 printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r));
2527 // we might have had a .TH, for example, since we last tried
2528 if (!(flags & NOKEEP) && row_begins_section(r))
2529 prints("." KEEP_MACRO_NAME "\n");
2530 printfs(".mk %1\n", row_start_reg(r));
2531 prints(".mk " BOTTOM_REG "\n"
2532 "." REPEATED_VPT_MACRO " 0\n");
2533 int c;
2534 int row_is_blank = 1;
2535 int first_start_row = r;
2536 for (c = 0; c < ncolumns; c++) {
2537 table_entry *e = entry[r][c];
2538 if (e) {
2539 if (e->end_row == r) {
2540 e->do_depth();
2541 if (e->start_row < first_start_row)
2542 first_start_row = e->start_row;
2543 row_is_blank = 0;
2545 c = e->end_col;
2548 if (row_is_blank)
2549 prints(".nr " BOTTOM_REG " +1v\n");
2550 if (row_is_all_lines[r]) {
2551 prints(".vs " LINE_SEP);
2552 if (row_is_all_lines[r] == 2)
2553 prints("+" DOUBLE_LINE_SEP);
2554 prints(">?\\n[.V]u\n.ls 1\n");
2555 prints("\\&");
2556 prints("\\v'" BODY_DEPTH);
2557 if (row_is_all_lines[r] == 2)
2558 prints("-" HALF_DOUBLE_LINE_SEP);
2559 prints("'");
2560 for (c = 0; c < ncolumns; c++) {
2561 table_entry *e = entry[r][c];
2562 if (e) {
2563 if (e->end_row == e->start_row)
2564 e->to_simple_entry()->simple_print(1);
2565 c = e->end_col;
2568 prints("\n");
2569 prints(".ls\n"
2570 ".vs\n");
2571 prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n");
2572 printfs(".sp |\\n[%1]u\n", row_start_reg(r));
2574 for (int i = row_is_all_lines[r] ? r - 1 : r;
2575 i >= first_start_row;
2576 i--) {
2577 simple_entry *first = 0;
2578 for (c = 0; c < ncolumns; c++) {
2579 table_entry *e = entry[r][c];
2580 if (e) {
2581 if (e->end_row == r && e->start_row == i) {
2582 simple_entry *simple = e->to_simple_entry();
2583 if (simple) {
2584 if (!first) {
2585 prints(".ta");
2586 first = simple;
2588 simple->add_tab();
2591 c = e->end_col;
2594 if (first) {
2595 prints('\n');
2596 first->position_vertically();
2597 first->set_location();
2598 prints("\\&");
2599 first->simple_print(0);
2600 for (c = first->end_col + 1; c < ncolumns; c++) {
2601 table_entry *e = entry[r][c];
2602 if (e) {
2603 if (e->end_row == r && e->start_row == i) {
2604 simple_entry *simple = e->to_simple_entry();
2605 if (simple)
2606 simple->simple_print(0);
2608 c = e->end_col;
2611 prints('\n');
2612 prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n");
2613 printfs(".sp |\\n[%1]u\n", row_start_reg(r));
2616 for (c = 0; c < ncolumns; c++) {
2617 table_entry *e = entry[r][c];
2618 if (e) {
2619 if (e->end_row == r && e->to_simple_entry() == 0) {
2620 e->position_vertically();
2621 e->print();
2622 prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n");
2623 printfs(".sp |\\n[%1]u\n", row_start_reg(r));
2625 c = e->end_col;
2628 prints("." REPEATED_VPT_MACRO " 1\n"
2629 ".sp |\\n[" BOTTOM_REG "]u\n"
2630 "\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG " 1\n");
2631 if (r != nrows - 1 && (flags & ALLBOX)) {
2632 print_single_hline(r + 1);
2633 prints("\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG " 0\n");
2635 if (r != nrows - 1) {
2636 if (p && p->row == r + 1
2637 && (p->is_single_line() || p->is_double_line())) {
2638 p->print(this);
2639 prints("\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG
2640 " 0\n");
2642 int printed_one = 0;
2643 for (vertical_rule *vr = vrule_list; vr; vr = vr->next)
2644 if (vr->end_row == r) {
2645 if (!printed_one) {
2646 prints("." REPEATED_VPT_MACRO " 0\n");
2647 printed_one = 1;
2649 vr->print();
2651 if (printed_one)
2652 prints("." REPEATED_VPT_MACRO " 1\n");
2653 if (!(flags & NOKEEP) && row_ends_section(r))
2654 prints("." RELEASE_MACRO_NAME "\n");
2658 void table::do_top()
2660 prints(".fc \002\003\n");
2661 if (!(flags & NOKEEP) && (flags & (BOX|DOUBLEBOX|ALLBOX)))
2662 prints("." TABLE_KEEP_MACRO_NAME "\n");
2663 if (flags & DOUBLEBOX) {
2664 prints(".ls 1\n"
2665 ".vs " LINE_SEP ">?\\n[.V]u\n"
2666 "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]\\D'l \\n[TW]u 0'\\s0\n"
2667 ".vs\n"
2668 "." REPEATED_MARK_MACRO " " TOP_REG "\n"
2669 ".vs " DOUBLE_LINE_SEP ">?\\n[.V]u\n");
2670 printfs("\\v'" BODY_DEPTH "'"
2671 "\\s[\\n[" LINESIZE_REG "]]"
2672 "\\h'\\n[%1]u'"
2673 "\\D'l |\\n[%2]u 0'"
2674 "\\s0"
2675 "\n",
2676 column_divide_reg(0),
2677 column_divide_reg(ncolumns));
2678 prints(".ls\n"
2679 ".vs\n");
2681 else if (flags & (ALLBOX|BOX)) {
2682 print_single_hline(0);
2684 //printfs(".mk %1\n", row_top_reg(0));
2687 void table::do_bottom()
2689 // print stuff after last row
2690 for (stuff *p = stuff_list; p; p = p->next)
2691 if (p->row > nrows - 1)
2692 p->print(this);
2693 if (!(flags & NOKEEP))
2694 prints("." RELEASE_MACRO_NAME "\n");
2695 printfs(".mk %1\n", row_top_reg(nrows));
2696 prints(".nr " NEED_BOTTOM_RULE_REG " 1\n"
2697 ".nr T. 1\n"
2698 ".T#\n");
2699 if (!(flags & NOKEEP) && (flags & (BOX|DOUBLEBOX|ALLBOX)))
2700 prints("." TABLE_RELEASE_MACRO_NAME "\n");
2701 if (flags & DOUBLEBOX)
2702 prints(".sp " DOUBLE_LINE_SEP "\n");
2703 prints("." RESET_MACRO_NAME "\n"
2704 ".fc\n"
2705 ".cp \\n(" COMPATIBLE_REG "\n");
2708 int table::get_nrows()
2710 return nrows;
2713 const char *last_filename = 0;
2715 void set_troff_location(const char *fn, int ln)
2717 if (!location_force_filename && last_filename != 0
2718 && strcmp(fn, last_filename) == 0)
2719 printfs(".lf %1\n", as_string(ln));
2720 else {
2721 printfs(".lf %1 %2\n", as_string(ln), fn);
2722 last_filename = fn;
2723 location_force_filename = 0;
2727 void printfs(const char *s, const string &arg1, const string &arg2,
2728 const string &arg3, const string &arg4, const string &arg5)
2730 if (s) {
2731 char c;
2732 while ((c = *s++) != '\0') {
2733 if (c == '%') {
2734 switch (*s++) {
2735 case '1':
2736 prints(arg1);
2737 break;
2738 case '2':
2739 prints(arg2);
2740 break;
2741 case '3':
2742 prints(arg3);
2743 break;
2744 case '4':
2745 prints(arg4);
2746 break;
2747 case '5':
2748 prints(arg5);
2749 break;
2750 case '6':
2751 case '7':
2752 case '8':
2753 case '9':
2754 break;
2755 case '%':
2756 prints('%');
2757 break;
2758 default:
2759 assert(0);
2762 else
2763 prints(c);