* All affected files: Update postal address of FSF.
[s-roff.git] / src / preproc / tbl / table.cpp
blob8312386aafeef920e8a13d061391174fed1358bf
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2003, 2004
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, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, 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 SEPARATION_FACTOR_REG PREFIX "sep"
34 #define BOTTOM_REG PREFIX "bot"
35 #define RESET_MACRO_NAME PREFIX "init"
36 #define LINESIZE_REG PREFIX "lps"
37 #define TOP_REG PREFIX "top"
38 #define CURRENT_ROW_REG PREFIX "crow"
39 #define LAST_PASSED_ROW_REG PREFIX "passed"
40 #define TRANSPARENT_STRING_NAME PREFIX "trans"
41 #define QUOTE_STRING_NAME PREFIX "quote"
42 #define SECTION_DIVERSION_NAME PREFIX "section"
43 #define SECTION_DIVERSION_FLAG_REG PREFIX "sflag"
44 #define SAVED_VERTICAL_POS_REG PREFIX "vert"
45 #define NEED_BOTTOM_RULE_REG PREFIX "brule"
46 #define KEEP_MACRO_NAME PREFIX "keep"
47 #define RELEASE_MACRO_NAME PREFIX "release"
48 #define SAVED_FONT_REG PREFIX "fnt"
49 #define SAVED_SIZE_REG PREFIX "sz"
50 #define SAVED_FILL_REG PREFIX "fll"
51 #define SAVED_INDENT_REG PREFIX "ind"
52 #define SAVED_CENTER_REG PREFIX "cent"
53 #define TABLE_DIVERSION_NAME PREFIX "table"
54 #define TABLE_DIVERSION_FLAG_REG PREFIX "tflag"
55 #define TABLE_KEEP_MACRO_NAME PREFIX "tkeep"
56 #define TABLE_RELEASE_MACRO_NAME PREFIX "trelease"
57 #define NEEDED_REG PREFIX "needed"
58 #define REPEATED_MARK_MACRO PREFIX "rmk"
59 #define REPEATED_VPT_MACRO PREFIX "rvpt"
60 #define SUPPRESS_BOTTOM_REG PREFIX "supbot"
61 #define SAVED_DN_REG PREFIX "dn"
63 // this must be one character
64 #define COMPATIBLE_REG PREFIX "c"
66 #define LEADER_REG PREFIX LEADER
68 #define BLOCK_WIDTH_PREFIX PREFIX "tbw"
69 #define BLOCK_DIVERSION_PREFIX PREFIX "tbd"
70 #define BLOCK_HEIGHT_PREFIX PREFIX "tbh"
71 #define SPAN_WIDTH_PREFIX PREFIX "w"
72 #define SPAN_LEFT_NUMERIC_WIDTH_PREFIX PREFIX "lnw"
73 #define SPAN_RIGHT_NUMERIC_WIDTH_PREFIX PREFIX "rnw"
74 #define SPAN_ALPHABETIC_WIDTH_PREFIX PREFIX "aw"
75 #define COLUMN_SEPARATION_PREFIX PREFIX "cs"
76 #define ROW_START_PREFIX PREFIX "rs"
77 #define COLUMN_START_PREFIX PREFIX "cl"
78 #define COLUMN_END_PREFIX PREFIX "ce"
79 #define COLUMN_DIVIDE_PREFIX PREFIX "cd"
80 #define ROW_TOP_PREFIX PREFIX "rt"
82 string block_width_reg(int r, int c);
83 string block_diversion_name(int r, int c);
84 string block_height_reg(int r, int c);
85 string span_width_reg(int start_col, int end_col);
86 string span_left_numeric_width_reg(int start_col, int end_col);
87 string span_right_numeric_width_reg(int start_col, int end_col);
88 string span_alphabetic_width_reg(int start_col, int end_col);
89 string column_separation_reg(int col);
90 string row_start_reg(int r);
91 string column_start_reg(int c);
92 string column_end_reg(int c);
93 string column_divide_reg(int c);
94 string row_top_reg(int r);
96 void set_inline_modifier(const entry_modifier *);
97 void restore_inline_modifier(const entry_modifier *m);
98 void set_modifier(const entry_modifier *);
99 int find_decimal_point(const char *s, char decimal_point_char,
100 const char *delim);
102 string an_empty_string;
103 int location_force_filename = 0;
105 void printfs(const char *,
106 const string &arg1 = an_empty_string,
107 const string &arg2 = an_empty_string,
108 const string &arg3 = an_empty_string,
109 const string &arg4 = an_empty_string,
110 const string &arg5 = an_empty_string);
112 void prints(const string &);
114 inline void prints(char c)
116 putchar(c);
119 inline void prints(const char *s)
121 fputs(s, stdout);
124 void prints(const string &s)
126 if (!s.empty())
127 fwrite(s.contents(), 1, s.length(), stdout);
130 struct horizontal_span {
131 horizontal_span *next;
132 int start_col;
133 int end_col;
134 horizontal_span(int, int, horizontal_span *);
137 class single_line_entry;
138 class double_line_entry;
139 class simple_entry;
141 class table_entry {
142 friend class table;
143 table_entry *next;
144 int input_lineno;
145 const char *input_filename;
146 protected:
147 int start_row;
148 int end_row;
149 int start_col;
150 int end_col;
151 const entry_modifier *mod;
152 public:
153 void set_location();
154 table_entry(const entry_modifier *);
155 virtual ~table_entry();
156 virtual int divert(int ncols, const string *mw, int *sep);
157 virtual void do_width();
158 virtual void do_depth();
159 virtual void print() = 0;
160 virtual void position_vertically() = 0;
161 virtual single_line_entry *to_single_line_entry();
162 virtual double_line_entry *to_double_line_entry();
163 virtual simple_entry *to_simple_entry();
164 virtual int line_type();
165 virtual void note_double_vrule_on_right(int);
166 virtual void note_double_vrule_on_left(int);
169 class simple_entry : public table_entry {
170 public:
171 simple_entry(const entry_modifier *);
172 void print();
173 void position_vertically();
174 simple_entry *to_simple_entry();
175 virtual void add_tab();
176 virtual void simple_print(int);
179 class empty_entry : public simple_entry {
180 public:
181 empty_entry(const entry_modifier *);
182 int line_type();
185 class text_entry : public simple_entry {
186 protected:
187 char *contents;
188 void print_contents();
189 public:
190 text_entry(char *, const entry_modifier *);
191 ~text_entry();
194 void text_entry::print_contents()
196 set_inline_modifier(mod);
197 prints(contents);
198 restore_inline_modifier(mod);
201 class repeated_char_entry : public text_entry {
202 public:
203 repeated_char_entry(char *s, const entry_modifier *m);
204 void simple_print(int);
207 class simple_text_entry : public text_entry {
208 public:
209 simple_text_entry(char *s, const entry_modifier *m);
210 void do_width();
213 class left_text_entry : public simple_text_entry {
214 public:
215 left_text_entry(char *s, const entry_modifier *m);
216 void simple_print(int);
217 void add_tab();
220 class right_text_entry : public simple_text_entry {
221 public:
222 right_text_entry(char *s, const entry_modifier *m);
223 void simple_print(int);
224 void add_tab();
227 class center_text_entry : public simple_text_entry {
228 public:
229 center_text_entry(char *s, const entry_modifier *m);
230 void simple_print(int);
231 void add_tab();
234 class numeric_text_entry : public text_entry {
235 int dot_pos;
236 public:
237 numeric_text_entry(char *s, const entry_modifier *m, int pos);
238 void do_width();
239 void simple_print(int);
242 class alphabetic_text_entry : public text_entry {
243 public:
244 alphabetic_text_entry(char *s, const entry_modifier *m);
245 void do_width();
246 void simple_print(int);
247 void add_tab();
250 class line_entry : public simple_entry {
251 protected:
252 char double_vrule_on_right;
253 char double_vrule_on_left;
254 public:
255 line_entry(const entry_modifier *);
256 void note_double_vrule_on_right(int);
257 void note_double_vrule_on_left(int);
258 void simple_print(int) = 0;
261 class single_line_entry : public line_entry {
262 public:
263 single_line_entry(const entry_modifier *m);
264 void simple_print(int);
265 single_line_entry *to_single_line_entry();
266 int line_type();
269 class double_line_entry : public line_entry {
270 public:
271 double_line_entry(const entry_modifier *m);
272 void simple_print(int);
273 double_line_entry *to_double_line_entry();
274 int line_type();
277 class short_line_entry : public simple_entry {
278 public:
279 short_line_entry(const entry_modifier *m);
280 void simple_print(int);
281 int line_type();
284 class short_double_line_entry : public simple_entry {
285 public:
286 short_double_line_entry(const entry_modifier *m);
287 void simple_print(int);
288 int line_type();
291 class block_entry : public table_entry {
292 char *contents;
293 protected:
294 void do_divert(int alphabetic, int ncols, const string *mw, int *sep);
295 public:
296 block_entry(char *s, const entry_modifier *m);
297 ~block_entry();
298 int divert(int ncols, const string *mw, int *sep);
299 void do_width();
300 void do_depth();
301 void position_vertically();
302 void print() = 0;
305 class left_block_entry : public block_entry {
306 public:
307 left_block_entry(char *s, const entry_modifier *m);
308 void print();
311 class right_block_entry : public block_entry {
312 public:
313 right_block_entry(char *s, const entry_modifier *m);
314 void print();
317 class center_block_entry : public block_entry {
318 public:
319 center_block_entry(char *s, const entry_modifier *m);
320 void print();
323 class alphabetic_block_entry : public block_entry {
324 public:
325 alphabetic_block_entry(char *s, const entry_modifier *m);
326 void print();
327 int divert(int ncols, const string *mw, int *sep);
330 table_entry::table_entry(const entry_modifier *m)
331 : next(0), input_lineno(-1), input_filename(0),
332 start_row(-1), end_row(-1), start_col(-1), end_col(-1), mod(m)
336 table_entry::~table_entry()
340 int table_entry::divert(int, const string *, int *)
342 return 0;
345 void table_entry::do_width()
349 single_line_entry *table_entry::to_single_line_entry()
351 return 0;
354 double_line_entry *table_entry::to_double_line_entry()
356 return 0;
359 simple_entry *table_entry::to_simple_entry()
361 return 0;
364 void table_entry::do_depth()
368 void table_entry::set_location()
370 set_troff_location(input_filename, input_lineno);
373 int table_entry::line_type()
375 return -1;
378 void table_entry::note_double_vrule_on_right(int)
382 void table_entry::note_double_vrule_on_left(int)
386 simple_entry::simple_entry(const entry_modifier *m) : table_entry(m)
390 void simple_entry::add_tab()
392 // do nothing
395 void simple_entry::simple_print(int)
397 // do nothing
400 void simple_entry::position_vertically()
402 if (start_row != end_row)
403 switch (mod->vertical_alignment) {
404 case entry_modifier::TOP:
405 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
406 break;
407 case entry_modifier::CENTER:
408 // Peform the motion in two stages so that the center is rounded
409 // vertically upwards even if net vertical motion is upwards.
410 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
411 printfs(".sp \\n[" BOTTOM_REG "]u-\\n[%1]u-1v/2u\n",
412 row_start_reg(start_row));
413 break;
414 case entry_modifier::BOTTOM:
415 printfs(".sp |\\n[%1]u+\\n[" BOTTOM_REG "]u-\\n[%1]u-1v\n",
416 row_start_reg(start_row));
417 break;
418 default:
419 assert(0);
423 void simple_entry::print()
425 prints(".ta");
426 add_tab();
427 prints('\n');
428 set_location();
429 prints("\\&");
430 simple_print(0);
431 prints('\n');
434 simple_entry *simple_entry::to_simple_entry()
436 return this;
439 empty_entry::empty_entry(const entry_modifier *m)
440 : simple_entry(m)
444 int empty_entry::line_type()
446 return 0;
449 text_entry::text_entry(char *s, const entry_modifier *m)
450 : simple_entry(m), contents(s)
454 text_entry::~text_entry()
456 a_delete contents;
459 repeated_char_entry::repeated_char_entry(char *s, const entry_modifier *m)
460 : text_entry(s, m)
464 void repeated_char_entry::simple_print(int)
466 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
467 set_inline_modifier(mod);
468 printfs("\\l" DELIMITER_CHAR "\\n[%1]u\\&",
469 span_width_reg(start_col, end_col));
470 prints(contents);
471 prints(DELIMITER_CHAR);
472 restore_inline_modifier(mod);
475 simple_text_entry::simple_text_entry(char *s, const entry_modifier *m)
476 : text_entry(s, m)
480 void simple_text_entry::do_width()
482 set_location();
483 printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR,
484 span_width_reg(start_col, end_col));
485 print_contents();
486 prints(DELIMITER_CHAR "\n");
489 left_text_entry::left_text_entry(char *s, const entry_modifier *m)
490 : simple_text_entry(s, m)
494 void left_text_entry::simple_print(int)
496 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
497 print_contents();
500 // The only point of this is to make `\a' ``work'' as in Unix tbl. Grrr.
502 void left_text_entry::add_tab()
504 printfs(" \\n[%1]u", column_end_reg(end_col));
507 right_text_entry::right_text_entry(char *s, const entry_modifier *m)
508 : simple_text_entry(s, m)
512 void right_text_entry::simple_print(int)
514 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
515 prints("\002\003");
516 print_contents();
517 prints("\002");
520 void right_text_entry::add_tab()
522 printfs(" \\n[%1]u", column_end_reg(end_col));
525 center_text_entry::center_text_entry(char *s, const entry_modifier *m)
526 : simple_text_entry(s, m)
530 void center_text_entry::simple_print(int)
532 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
533 prints("\002\003");
534 print_contents();
535 prints("\003\002");
538 void center_text_entry::add_tab()
540 printfs(" \\n[%1]u", column_end_reg(end_col));
543 numeric_text_entry::numeric_text_entry(char *s, const entry_modifier *m, int pos)
544 : text_entry(s, m), dot_pos(pos)
548 void numeric_text_entry::do_width()
550 if (dot_pos != 0) {
551 set_location();
552 printfs(".nr %1 0\\w" DELIMITER_CHAR,
553 block_width_reg(start_row, start_col));
554 set_inline_modifier(mod);
555 for (int i = 0; i < dot_pos; i++)
556 prints(contents[i]);
557 restore_inline_modifier(mod);
558 prints(DELIMITER_CHAR "\n");
559 printfs(".nr %1 \\n[%1]>?\\n[%2]\n",
560 span_left_numeric_width_reg(start_col, end_col),
561 block_width_reg(start_row, start_col));
563 else
564 printfs(".nr %1 0\n", block_width_reg(start_row, start_col));
565 if (contents[dot_pos] != '\0') {
566 set_location();
567 printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR,
568 span_right_numeric_width_reg(start_col, end_col));
569 set_inline_modifier(mod);
570 prints(contents + dot_pos);
571 restore_inline_modifier(mod);
572 prints(DELIMITER_CHAR "\n");
576 void numeric_text_entry::simple_print(int)
578 printfs("\\h'|(\\n[%1]u-\\n[%2]u-\\n[%3]u/2u+\\n[%2]u+\\n[%4]u-\\n[%5]u)'",
579 span_width_reg(start_col, end_col),
580 span_left_numeric_width_reg(start_col, end_col),
581 span_right_numeric_width_reg(start_col, end_col),
582 column_start_reg(start_col),
583 block_width_reg(start_row, start_col));
584 print_contents();
587 alphabetic_text_entry::alphabetic_text_entry(char *s, const entry_modifier *m)
588 : text_entry(s, m)
592 void alphabetic_text_entry::do_width()
594 set_location();
595 printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR,
596 span_alphabetic_width_reg(start_col, end_col));
597 print_contents();
598 prints(DELIMITER_CHAR "\n");
601 void alphabetic_text_entry::simple_print(int)
603 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
604 printfs("\\h'\\n[%1]u-\\n[%2]u/2u'",
605 span_width_reg(start_col, end_col),
606 span_alphabetic_width_reg(start_col, end_col));
607 print_contents();
610 // The only point of this is to make `\a' ``work'' as in Unix tbl. Grrr.
612 void alphabetic_text_entry::add_tab()
614 printfs(" \\n[%1]u", column_end_reg(end_col));
617 block_entry::block_entry(char *s, const entry_modifier *m)
618 : table_entry(m), contents(s)
622 block_entry::~block_entry()
624 a_delete contents;
627 void block_entry::position_vertically()
629 if (start_row != end_row)
630 switch(mod->vertical_alignment) {
631 case entry_modifier::TOP:
632 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
633 break;
634 case entry_modifier::CENTER:
635 // Peform the motion in two stages so that the center is rounded
636 // vertically upwards even if net vertical motion is upwards.
637 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
638 printfs(".sp \\n[" BOTTOM_REG "]u-\\n[%1]u-\\n[%2]u/2u\n",
639 row_start_reg(start_row),
640 block_height_reg(start_row, start_col));
641 break;
642 case entry_modifier::BOTTOM:
643 printfs(".sp |\\n[%1]u+\\n[" BOTTOM_REG "]u-\\n[%1]u-\\n[%2]u\n",
644 row_start_reg(start_row),
645 block_height_reg(start_row, start_col));
646 break;
647 default:
648 assert(0);
650 if (mod->stagger)
651 prints(".sp -.5v\n");
654 int block_entry::divert(int ncols, const string *mw, int *sep)
656 do_divert(0, ncols, mw, sep);
657 return 1;
660 void block_entry::do_divert(int alphabetic, int ncols, const string *mw,
661 int *sep)
663 printfs(".di %1\n", block_diversion_name(start_row, start_col));
664 prints(".if \\n[" SAVED_FILL_REG "] .fi\n"
665 ".in 0\n");
666 prints(".ll ");
667 int i;
668 for (i = start_col; i <= end_col; i++)
669 if (mw[i].empty())
670 break;
671 if (i > end_col) {
672 // Every column spanned by this entry has a minimum width.
673 for (int j = start_col; j <= end_col; j++) {
674 if (j > start_col) {
675 if (sep)
676 printfs("+%1n", as_string(sep[j - 1]));
677 prints('+');
679 printfs("(n;%1)", mw[j]);
681 printfs(">?\\n[%1]u", span_width_reg(start_col, end_col));
683 else
684 printfs("(u;\\n[%1]>?(\\n[.l]*%2/%3))",
685 span_width_reg(start_col, end_col),
686 as_string(end_col - start_col + 1),
687 as_string(ncols + 1));
688 if (alphabetic)
689 prints("-2n");
690 prints("\n");
691 prints(".cp \\n(" COMPATIBLE_REG "\n");
692 set_modifier(mod);
693 set_location();
694 prints(contents);
695 prints(".br\n.di\n.cp 0\n");
696 if (!mod->zero_width) {
697 if (alphabetic) {
698 printfs(".nr %1 \\n[%1]>?(\\n[dl]+2n)\n",
699 span_width_reg(start_col, end_col));
700 printfs(".nr %1 \\n[%1]>?\\n[dl]\n",
701 span_alphabetic_width_reg(start_col, end_col));
703 else
704 printfs(".nr %1 \\n[%1]>?\\n[dl]\n", span_width_reg(start_col, end_col));
706 printfs(".nr %1 \\n[dn]\n", block_height_reg(start_row, start_col));
707 printfs(".nr %1 \\n[dl]\n", block_width_reg(start_row, start_col));
708 prints("." RESET_MACRO_NAME "\n"
709 ".in \\n[" SAVED_INDENT_REG "]u\n"
710 ".nf\n");
711 // the block might have contained .lf commands
712 location_force_filename = 1;
715 void block_entry::do_width()
717 // do nothing; the action happens in divert
720 void block_entry::do_depth()
722 printfs(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?(\\n[%1]+\\n[%2])\n",
723 row_start_reg(start_row),
724 block_height_reg(start_row, start_col));
727 left_block_entry::left_block_entry(char *s, const entry_modifier *m)
728 : block_entry(s, m)
732 void left_block_entry::print()
734 printfs(".in +\\n[%1]u\n", column_start_reg(start_col));
735 printfs(".%1\n", block_diversion_name(start_row, start_col));
736 prints(".in\n");
739 right_block_entry::right_block_entry(char *s, const entry_modifier *m)
740 : block_entry(s, m)
744 void right_block_entry::print()
746 printfs(".in +\\n[%1]u+\\n[%2]u-\\n[%3]u\n",
747 column_start_reg(start_col),
748 span_width_reg(start_col, end_col),
749 block_width_reg(start_row, start_col));
750 printfs(".%1\n", block_diversion_name(start_row, start_col));
751 prints(".in\n");
754 center_block_entry::center_block_entry(char *s, const entry_modifier *m)
755 : block_entry(s, m)
759 void center_block_entry::print()
761 printfs(".in +\\n[%1]u+(\\n[%2]u-\\n[%3]u/2u)\n",
762 column_start_reg(start_col),
763 span_width_reg(start_col, end_col),
764 block_width_reg(start_row, start_col));
765 printfs(".%1\n", block_diversion_name(start_row, start_col));
766 prints(".in\n");
769 alphabetic_block_entry::alphabetic_block_entry(char *s,
770 const entry_modifier *m)
771 : block_entry(s, m)
775 int alphabetic_block_entry::divert(int ncols, const string *mw, int *sep)
777 do_divert(1, ncols, mw, sep);
778 return 1;
781 void alphabetic_block_entry::print()
783 printfs(".in +\\n[%1]u+(\\n[%2]u-\\n[%3]u/2u)\n",
784 column_start_reg(start_col),
785 span_width_reg(start_col, end_col),
786 span_alphabetic_width_reg(start_col, end_col));
787 printfs(".%1\n", block_diversion_name(start_row, start_col));
788 prints(".in\n");
791 line_entry::line_entry(const entry_modifier *m)
792 : simple_entry(m), double_vrule_on_right(0), double_vrule_on_left(0)
796 void line_entry::note_double_vrule_on_right(int is_corner)
798 double_vrule_on_right = is_corner ? 1 : 2;
801 void line_entry::note_double_vrule_on_left(int is_corner)
803 double_vrule_on_left = is_corner ? 1 : 2;
806 single_line_entry::single_line_entry(const entry_modifier *m)
807 : line_entry(m)
811 int single_line_entry::line_type()
813 return 1;
816 void single_line_entry::simple_print(int dont_move)
818 printfs("\\h'|\\n[%1]u",
819 column_divide_reg(start_col));
820 if (double_vrule_on_left) {
821 prints(double_vrule_on_left == 1 ? "-" : "+");
822 prints(HALF_DOUBLE_LINE_SEP);
824 prints("'");
825 if (!dont_move)
826 prints("\\v'-" BAR_HEIGHT "'");
827 printfs("\\s[\\n[" LINESIZE_REG "]]" "\\D'l |\\n[%1]u",
828 column_divide_reg(end_col+1));
829 if (double_vrule_on_right) {
830 prints(double_vrule_on_left == 1 ? "+" : "-");
831 prints(HALF_DOUBLE_LINE_SEP);
833 prints("0'\\s0");
834 if (!dont_move)
835 prints("\\v'" BAR_HEIGHT "'");
838 single_line_entry *single_line_entry::to_single_line_entry()
840 return this;
843 double_line_entry::double_line_entry(const entry_modifier *m)
844 : line_entry(m)
848 int double_line_entry::line_type()
850 return 2;
853 void double_line_entry::simple_print(int dont_move)
855 if (!dont_move)
856 prints("\\v'-" BAR_HEIGHT "'");
857 printfs("\\h'|\\n[%1]u",
858 column_divide_reg(start_col));
859 if (double_vrule_on_left) {
860 prints(double_vrule_on_left == 1 ? "-" : "+");
861 prints(HALF_DOUBLE_LINE_SEP);
863 prints("'");
864 printfs("\\v'-" HALF_DOUBLE_LINE_SEP "'"
865 "\\s[\\n[" LINESIZE_REG "]]"
866 "\\D'l |\\n[%1]u",
867 column_divide_reg(end_col+1));
868 if (double_vrule_on_right)
869 prints("-" HALF_DOUBLE_LINE_SEP);
870 prints(" 0'");
871 printfs("\\v'" DOUBLE_LINE_SEP "'"
872 "\\D'l |\\n[%1]u",
873 column_divide_reg(start_col));
874 if (double_vrule_on_right) {
875 prints(double_vrule_on_left == 1 ? "+" : "-");
876 prints(HALF_DOUBLE_LINE_SEP);
878 prints(" 0'");
879 prints("\\s0"
880 "\\v'-" HALF_DOUBLE_LINE_SEP "'");
881 if (!dont_move)
882 prints("\\v'" BAR_HEIGHT "'");
885 double_line_entry *double_line_entry::to_double_line_entry()
887 return this;
890 short_line_entry::short_line_entry(const entry_modifier *m)
891 : simple_entry(m)
895 int short_line_entry::line_type()
897 return 1;
900 void short_line_entry::simple_print(int dont_move)
902 if (mod->stagger)
903 prints("\\v'-.5v'");
904 if (!dont_move)
905 prints("\\v'-" BAR_HEIGHT "'");
906 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
907 printfs("\\s[\\n[" LINESIZE_REG "]]"
908 "\\D'l \\n[%1]u 0'"
909 "\\s0",
910 span_width_reg(start_col, end_col));
911 if (!dont_move)
912 prints("\\v'" BAR_HEIGHT "'");
913 if (mod->stagger)
914 prints("\\v'.5v'");
917 short_double_line_entry::short_double_line_entry(const entry_modifier *m)
918 : simple_entry(m)
922 int short_double_line_entry::line_type()
924 return 2;
927 void short_double_line_entry::simple_print(int dont_move)
929 if (mod->stagger)
930 prints("\\v'-.5v'");
931 if (!dont_move)
932 prints("\\v'-" BAR_HEIGHT "'");
933 printfs("\\h'|\\n[%2]u'"
934 "\\v'-" HALF_DOUBLE_LINE_SEP "'"
935 "\\s[\\n[" LINESIZE_REG "]]"
936 "\\D'l \\n[%1]u 0'"
937 "\\v'" DOUBLE_LINE_SEP "'"
938 "\\D'l |\\n[%2]u 0'"
939 "\\s0"
940 "\\v'-" HALF_DOUBLE_LINE_SEP "'",
941 span_width_reg(start_col, end_col),
942 column_start_reg(start_col));
943 if (!dont_move)
944 prints("\\v'" BAR_HEIGHT "'");
945 if (mod->stagger)
946 prints("\\v'.5v'");
949 void set_modifier(const entry_modifier *m)
951 if (!m->font.empty())
952 printfs(".ft %1\n", m->font);
953 if (m->point_size.val != 0) {
954 prints(".ps ");
955 if (m->point_size.inc > 0)
956 prints('+');
957 else if (m->point_size.inc < 0)
958 prints('-');
959 printfs("%1\n", as_string(m->point_size.val));
961 if (m->vertical_spacing.val != 0) {
962 prints(".vs ");
963 if (m->vertical_spacing.inc > 0)
964 prints('+');
965 else if (m->vertical_spacing.inc < 0)
966 prints('-');
967 printfs("%1\n", as_string(m->vertical_spacing.val));
969 if (!m->macro.empty())
970 printfs(".%1\n", m->macro);
973 void set_inline_modifier(const entry_modifier *m)
975 if (!m->font.empty())
976 printfs("\\f[%1]", m->font);
977 if (m->point_size.val != 0) {
978 prints("\\s[");
979 if (m->point_size.inc > 0)
980 prints('+');
981 else if (m->point_size.inc < 0)
982 prints('-');
983 printfs("%1]", as_string(m->point_size.val));
985 if (m->stagger)
986 prints("\\v'-.5v'");
989 void restore_inline_modifier(const entry_modifier *m)
991 if (!m->font.empty())
992 prints("\\f[\\n[" SAVED_FONT_REG "]]");
993 if (m->point_size.val != 0)
994 prints("\\s[\\n[" SAVED_SIZE_REG "]]");
995 if (m->stagger)
996 prints("\\v'.5v'");
999 struct stuff {
1000 stuff *next;
1001 int row; // occurs before row `row'
1002 char printed; // has it been printed?
1004 stuff(int);
1005 virtual void print(table *) = 0;
1006 virtual ~stuff();
1007 virtual int is_single_line() { return 0; };
1008 virtual int is_double_line() { return 0; };
1011 stuff::stuff(int r) : next(0), row(r), printed(0)
1015 stuff::~stuff()
1019 struct text_stuff : public stuff {
1020 string contents;
1021 const char *filename;
1022 int lineno;
1024 text_stuff(const string &, int r, const char *fn, int ln);
1025 ~text_stuff();
1026 void print(table *);
1029 text_stuff::text_stuff(const string &s, int r, const char *fn, int ln)
1030 : stuff(r), contents(s), filename(fn), lineno(ln)
1034 text_stuff::~text_stuff()
1038 void text_stuff::print(table *)
1040 printed = 1;
1041 prints(".cp \\n(" COMPATIBLE_REG "\n");
1042 set_troff_location(filename, lineno);
1043 prints(contents);
1044 prints(".cp 0\n");
1045 location_force_filename = 1; // it might have been a .lf command
1048 struct single_hline_stuff : public stuff {
1049 single_hline_stuff(int r);
1050 void print(table *);
1051 int is_single_line();
1054 single_hline_stuff::single_hline_stuff(int r) : stuff(r)
1058 void single_hline_stuff::print(table *tbl)
1060 printed = 1;
1061 tbl->print_single_hline(row);
1064 int single_hline_stuff::is_single_line()
1066 return 1;
1069 struct double_hline_stuff : stuff {
1070 double_hline_stuff(int r);
1071 void print(table *);
1072 int is_double_line();
1075 double_hline_stuff::double_hline_stuff(int r) : stuff(r)
1079 void double_hline_stuff::print(table *tbl)
1081 printed = 1;
1082 tbl->print_double_hline(row);
1085 int double_hline_stuff::is_double_line()
1087 return 1;
1090 struct vertical_rule {
1091 vertical_rule *next;
1092 int start_row;
1093 int end_row;
1094 int col;
1095 char is_double;
1096 string top_adjust;
1097 string bot_adjust;
1099 vertical_rule(int sr, int er, int c, int dbl, vertical_rule *);
1100 ~vertical_rule();
1101 void contribute_to_bottom_macro(table *);
1102 void print();
1105 vertical_rule::vertical_rule(int sr, int er, int c, int dbl, vertical_rule *p)
1106 : next(p), start_row(sr), end_row(er), col(c), is_double(dbl)
1110 vertical_rule::~vertical_rule()
1114 void vertical_rule::contribute_to_bottom_macro(table *tbl)
1116 printfs(".if \\n[" CURRENT_ROW_REG "]>=%1",
1117 as_string(start_row));
1118 if (end_row != tbl->get_nrows() - 1)
1119 printfs("&(\\n[" CURRENT_ROW_REG "]<%1)",
1120 as_string(end_row));
1121 prints(" \\{");
1122 printfs(".if %1<=\\n[" LAST_PASSED_ROW_REG "] .nr %2 \\n[#T]\n",
1123 as_string(start_row),
1124 row_top_reg(start_row));
1125 const char *offset_table[3];
1126 if (is_double) {
1127 offset_table[0] = "-" HALF_DOUBLE_LINE_SEP;
1128 offset_table[1] = "+" HALF_DOUBLE_LINE_SEP;
1129 offset_table[2] = 0;
1131 else {
1132 offset_table[0] = "";
1133 offset_table[1] = 0;
1135 for (const char **offsetp = offset_table; *offsetp; offsetp++) {
1136 prints(".sp -1\n"
1137 "\\v'" BODY_DEPTH);
1138 if (!bot_adjust.empty())
1139 printfs("+%1", bot_adjust);
1140 prints("'");
1141 printfs("\\h'\\n[%1]u%3'\\s[\\n[" LINESIZE_REG "]]\\D'l 0 |\\n[%2]u-1v",
1142 column_divide_reg(col),
1143 row_top_reg(start_row),
1144 *offsetp);
1145 if (!bot_adjust.empty())
1146 printfs("-(%1)", bot_adjust);
1147 // don't perform the top adjustment if the top is actually #T
1148 if (!top_adjust.empty())
1149 printfs("+((%1)*(%2>\\n[" LAST_PASSED_ROW_REG "]))",
1150 top_adjust,
1151 as_string(start_row));
1152 prints("'\\s0\n");
1154 prints(".\\}\n");
1157 void vertical_rule::print()
1159 printfs("\\*[" TRANSPARENT_STRING_NAME "]"
1160 ".if %1<=\\*[" QUOTE_STRING_NAME "]\\n[" LAST_PASSED_ROW_REG "] "
1161 ".nr %2 \\*[" QUOTE_STRING_NAME "]\\n[#T]\n",
1162 as_string(start_row),
1163 row_top_reg(start_row));
1164 const char *offset_table[3];
1165 if (is_double) {
1166 offset_table[0] = "-" HALF_DOUBLE_LINE_SEP;
1167 offset_table[1] = "+" HALF_DOUBLE_LINE_SEP;
1168 offset_table[2] = 0;
1170 else {
1171 offset_table[0] = "";
1172 offset_table[1] = 0;
1174 for (const char **offsetp = offset_table; *offsetp; offsetp++) {
1175 prints("\\*[" TRANSPARENT_STRING_NAME "].sp -1\n"
1176 "\\*[" TRANSPARENT_STRING_NAME "]\\v'" BODY_DEPTH);
1177 if (!bot_adjust.empty())
1178 printfs("+%1", bot_adjust);
1179 prints("'");
1180 printfs("\\h'\\n[%1]u%3'"
1181 "\\s[\\n[" LINESIZE_REG "]]"
1182 "\\D'l 0 |\\*[" QUOTE_STRING_NAME "]\\n[%2]u-1v",
1183 column_divide_reg(col),
1184 row_top_reg(start_row),
1185 *offsetp);
1186 if (!bot_adjust.empty())
1187 printfs("-(%1)", bot_adjust);
1188 // don't perform the top adjustment if the top is actually #T
1189 if (!top_adjust.empty())
1190 printfs("+((%1)*(%2>\\*[" QUOTE_STRING_NAME "]\\n["
1191 LAST_PASSED_ROW_REG "]))",
1192 top_adjust,
1193 as_string(start_row));
1194 prints("'"
1195 "\\s0\n");
1199 table::table(int nc, unsigned f, int ls, char dpc)
1200 : flags(f), nrows(0), ncolumns(nc), linesize(ls), decimal_point_char(dpc),
1201 vrule_list(0), stuff_list(0), span_list(0),
1202 entry_list(0), entry_list_tailp(&entry_list), entry(0),
1203 vline(0), row_is_all_lines(0), left_separation(0), right_separation(0),
1204 allocated_rows(0)
1206 minimum_width = new string[ncolumns];
1207 column_separation = ncolumns > 1 ? new int[ncolumns - 1] : 0;
1208 equal = new char[ncolumns];
1209 int i;
1210 for (i = 0; i < ncolumns; i++)
1211 equal[i] = 0;
1212 for (i = 0; i < ncolumns-1; i++)
1213 column_separation[i] = DEFAULT_COLUMN_SEPARATION;
1214 delim[0] = delim[1] = '\0';
1217 table::~table()
1219 for (int i = 0; i < nrows; i++) {
1220 a_delete entry[i];
1221 a_delete vline[i];
1223 a_delete entry;
1224 a_delete vline;
1225 while (entry_list) {
1226 table_entry *tem = entry_list;
1227 entry_list = entry_list->next;
1228 delete tem;
1230 ad_delete(ncolumns) minimum_width;
1231 a_delete column_separation;
1232 a_delete equal;
1233 while (stuff_list) {
1234 stuff *tem = stuff_list;
1235 stuff_list = stuff_list->next;
1236 delete tem;
1238 while (vrule_list) {
1239 vertical_rule *tem = vrule_list;
1240 vrule_list = vrule_list->next;
1241 delete tem;
1243 a_delete row_is_all_lines;
1244 while (span_list) {
1245 horizontal_span *tem = span_list;
1246 span_list = span_list->next;
1247 delete tem;
1251 void table::set_delim(char c1, char c2)
1253 delim[0] = c1;
1254 delim[1] = c2;
1257 void table::set_minimum_width(int c, const string &w)
1259 assert(c >= 0 && c < ncolumns);
1260 minimum_width[c] = w;
1263 void table::set_column_separation(int c, int n)
1265 assert(c >= 0 && c < ncolumns - 1);
1266 column_separation[c] = n;
1269 void table::set_equal_column(int c)
1271 assert(c >= 0 && c < ncolumns);
1272 equal[c] = 1;
1275 void table::add_stuff(stuff *p)
1277 stuff **pp;
1278 for (pp = &stuff_list; *pp; pp = &(*pp)->next)
1280 *pp = p;
1283 void table::add_text_line(int r, const string &s, const char *filename, int lineno)
1285 add_stuff(new text_stuff(s, r, filename, lineno));
1288 void table::add_single_hline(int r)
1290 add_stuff(new single_hline_stuff(r));
1293 void table::add_double_hline(int r)
1295 add_stuff(new double_hline_stuff(r));
1298 void table::allocate(int r)
1300 if (r >= nrows) {
1301 typedef table_entry **PPtable_entry; // work around g++ 1.36.1 bug
1302 if (r >= allocated_rows) {
1303 if (allocated_rows == 0) {
1304 allocated_rows = 16;
1305 if (allocated_rows <= r)
1306 allocated_rows = r + 1;
1307 entry = new PPtable_entry[allocated_rows];
1308 vline = new char*[allocated_rows];
1310 else {
1311 table_entry ***old_entry = entry;
1312 int old_allocated_rows = allocated_rows;
1313 allocated_rows *= 2;
1314 if (allocated_rows <= r)
1315 allocated_rows = r + 1;
1316 entry = new PPtable_entry[allocated_rows];
1317 memcpy(entry, old_entry, sizeof(table_entry**)*old_allocated_rows);
1318 a_delete old_entry;
1319 char **old_vline = vline;
1320 vline = new char*[allocated_rows];
1321 memcpy(vline, old_vline, sizeof(char*)*old_allocated_rows);
1322 a_delete old_vline;
1325 assert(allocated_rows > r);
1326 while (nrows <= r) {
1327 entry[nrows] = new table_entry*[ncolumns];
1328 int i;
1329 for (i = 0; i < ncolumns; i++)
1330 entry[nrows][i] = 0;
1331 vline[nrows] = new char[ncolumns+1];
1332 for (i = 0; i < ncolumns+1; i++)
1333 vline[nrows][i] = 0;
1334 nrows++;
1339 void table::do_hspan(int r, int c)
1341 assert(r >= 0 && c >= 0 && r < nrows && c < ncolumns);
1342 if (c == 0) {
1343 error("first column cannot be horizontally spanned");
1344 return;
1346 table_entry *e = entry[r][c];
1347 if (e) {
1348 assert(e->start_row <= r && r <= e->end_row
1349 && e->start_col <= c && c <= e->end_col
1350 && e->end_row - e->start_row > 0
1351 && e->end_col - e->start_col > 0);
1352 return;
1354 e = entry[r][c-1];
1355 // e can be 0 if we had an empty entry or an error
1356 if (e == 0)
1357 return;
1358 if (e->start_row != r) {
1361 ^ s */
1362 error("impossible horizontal span at row %1, column %2", r + 1, c + 1);
1364 else {
1365 e->end_col = c;
1366 entry[r][c] = e;
1370 void table::do_vspan(int r, int c)
1372 assert(r >= 0 && c >= 0 && r < nrows && c < ncolumns);
1373 if (r == 0) {
1374 error("first row cannot be vertically spanned");
1375 return;
1377 table_entry *e = entry[r][c];
1378 if (e) {
1379 assert(e->start_row <= r && r <= e->end_row
1380 && e->start_col <= c && c <= e->end_col
1381 && e->end_row - e->start_row > 0
1382 && e->end_col - e->start_col > 0);
1383 return;
1385 e = entry[r-1][c];
1386 // e can be 0 if we had an empty entry or an error
1387 if (e == 0)
1388 return;
1389 if (e->start_col != c) {
1390 /* l s
1391 l ^ */
1392 error("impossible vertical span at row %1, column %2", r + 1, c + 1);
1394 else {
1395 for (int i = c; i <= e->end_col; i++) {
1396 assert(entry[r][i] == 0);
1397 entry[r][i] = e;
1399 e->end_row = r;
1403 int find_decimal_point(const char *s, char decimal_point_char,
1404 const char *delim)
1406 if (s == 0 || *s == '\0')
1407 return -1;
1408 const char *p;
1409 int in_delim = 0; // is p within eqn delimiters?
1410 // tbl recognises \& even within eqn delimiters; I don't
1411 for (p = s; *p; p++)
1412 if (in_delim) {
1413 if (*p == delim[1])
1414 in_delim = 0;
1416 else if (*p == delim[0])
1417 in_delim = 1;
1418 else if (p[0] == '\\' && p[1] == '&')
1419 return p - s;
1420 int possible_pos = -1;
1421 in_delim = 0;
1422 for (p = s; *p; p++)
1423 if (in_delim) {
1424 if (*p == delim[1])
1425 in_delim = 0;
1427 else if (*p == delim[0])
1428 in_delim = 1;
1429 else if (p[0] == decimal_point_char && csdigit(p[1]))
1430 possible_pos = p - s;
1431 if (possible_pos >= 0)
1432 return possible_pos;
1433 in_delim = 0;
1434 for (p = s; *p; p++)
1435 if (in_delim) {
1436 if (*p == delim[1])
1437 in_delim = 0;
1439 else if (*p == delim[0])
1440 in_delim = 1;
1441 else if (csdigit(*p))
1442 possible_pos = p + 1 - s;
1443 return possible_pos;
1446 void table::add_entry(int r, int c, const string &str, const entry_format *f,
1447 const char *fn, int ln)
1449 allocate(r);
1450 table_entry *e = 0;
1451 if (str == "\\_") {
1452 e = new short_line_entry(f);
1454 else if (str == "\\=") {
1455 e = new short_double_line_entry(f);
1457 else if (str == "_") {
1458 single_line_entry *lefte;
1459 if (c > 0 && entry[r][c-1] != 0 &&
1460 (lefte = entry[r][c-1]->to_single_line_entry()) != 0
1461 && lefte->start_row == r
1462 && lefte->mod->stagger == f->stagger) {
1463 lefte->end_col = c;
1464 entry[r][c] = lefte;
1466 else
1467 e = new single_line_entry(f);
1469 else if (str == "=") {
1470 double_line_entry *lefte;
1471 if (c > 0 && entry[r][c-1] != 0 &&
1472 (lefte = entry[r][c-1]->to_double_line_entry()) != 0
1473 && lefte->start_row == r
1474 && lefte->mod->stagger == f->stagger) {
1475 lefte->end_col = c;
1476 entry[r][c] = lefte;
1478 else
1479 e = new double_line_entry(f);
1481 else if (str == "\\^") {
1482 do_vspan(r, c);
1484 else if (str.length() > 2 && str[0] == '\\' && str[1] == 'R') {
1485 if (str.search('\n') >= 0)
1486 error_with_file_and_line(fn, ln, "bad repeated character");
1487 else {
1488 char *s = str.substring(2, str.length() - 2).extract();
1489 e = new repeated_char_entry(s, f);
1492 else {
1493 int is_block = str.search('\n') >= 0;
1494 char *s;
1495 switch (f->type) {
1496 case FORMAT_SPAN:
1497 assert(str.empty());
1498 do_hspan(r, c);
1499 break;
1500 case FORMAT_LEFT:
1501 if (!str.empty()) {
1502 s = str.extract();
1503 if (is_block)
1504 e = new left_block_entry(s, f);
1505 else
1506 e = new left_text_entry(s, f);
1508 else
1509 e = new empty_entry(f);
1510 break;
1511 case FORMAT_CENTER:
1512 if (!str.empty()) {
1513 s = str.extract();
1514 if (is_block)
1515 e = new center_block_entry(s, f);
1516 else
1517 e = new center_text_entry(s, f);
1519 else
1520 e = new empty_entry(f);
1521 break;
1522 case FORMAT_RIGHT:
1523 if (!str.empty()) {
1524 s = str.extract();
1525 if (is_block)
1526 e = new right_block_entry(s, f);
1527 else
1528 e = new right_text_entry(s, f);
1530 else
1531 e = new empty_entry(f);
1532 break;
1533 case FORMAT_NUMERIC:
1534 if (!str.empty()) {
1535 s = str.extract();
1536 if (is_block) {
1537 error_with_file_and_line(fn, ln, "can't have numeric text block");
1538 e = new left_block_entry(s, f);
1540 else {
1541 int pos = find_decimal_point(s, decimal_point_char, delim);
1542 if (pos < 0)
1543 e = new center_text_entry(s, f);
1544 else
1545 e = new numeric_text_entry(s, f, pos);
1548 else
1549 e = new empty_entry(f);
1550 break;
1551 case FORMAT_ALPHABETIC:
1552 if (!str.empty()) {
1553 s = str.extract();
1554 if (is_block)
1555 e = new alphabetic_block_entry(s, f);
1556 else
1557 e = new alphabetic_text_entry(s, f);
1559 else
1560 e = new empty_entry(f);
1561 break;
1562 case FORMAT_VSPAN:
1563 do_vspan(r, c);
1564 break;
1565 case FORMAT_HLINE:
1566 if (str.length() != 0)
1567 error_with_file_and_line(fn, ln,
1568 "non-empty data entry for `_' format ignored");
1569 e = new single_line_entry(f);
1570 break;
1571 case FORMAT_DOUBLE_HLINE:
1572 if (str.length() != 0)
1573 error_with_file_and_line(fn, ln,
1574 "non-empty data entry for `=' format ignored");
1575 e = new double_line_entry(f);
1576 break;
1577 default:
1578 assert(0);
1581 if (e) {
1582 table_entry *preve = entry[r][c];
1583 if (preve) {
1584 /* c s
1585 ^ l */
1586 error_with_file_and_line(fn, ln, "row %1, column %2 already spanned",
1587 r + 1, c + 1);
1588 delete e;
1590 else {
1591 e->input_lineno = ln;
1592 e->input_filename = fn;
1593 e->start_row = e->end_row = r;
1594 e->start_col = e->end_col = c;
1595 *entry_list_tailp = e;
1596 entry_list_tailp = &e->next;
1597 entry[r][c] = e;
1602 // add vertical lines for row r
1604 void table::add_vlines(int r, const char *v)
1606 allocate(r);
1607 for (int i = 0; i < ncolumns+1; i++)
1608 vline[r][i] = v[i];
1611 void table::check()
1613 table_entry *p = entry_list;
1614 int i, j;
1615 while (p) {
1616 for (i = p->start_row; i <= p->end_row; i++)
1617 for (j = p->start_col; j <= p->end_col; j++)
1618 assert(entry[i][j] == p);
1619 p = p->next;
1623 void table::print()
1625 location_force_filename = 1;
1626 check();
1627 init_output();
1628 determine_row_type();
1629 compute_widths();
1630 if (!(flags & CENTER))
1631 prints(".if \\n[" SAVED_CENTER_REG "] \\{");
1632 prints(".in +(u;\\n[.l]-\\n[.i]-\\n[TW]/2>?-\\n[.i])\n"
1633 ".nr " SAVED_INDENT_REG " \\n[.i]\n");
1634 if (!(flags & CENTER))
1635 prints(".\\}\n");
1636 build_vrule_list();
1637 define_bottom_macro();
1638 do_top();
1639 for (int i = 0; i < nrows; i++)
1640 do_row(i);
1641 do_bottom();
1644 void table::determine_row_type()
1646 row_is_all_lines = new char[nrows];
1647 for (int i = 0; i < nrows; i++) {
1648 int had_single = 0;
1649 int had_double = 0;
1650 int had_non_line = 0;
1651 for (int c = 0; c < ncolumns; c++) {
1652 table_entry *e = entry[i][c];
1653 if (e != 0) {
1654 if (e->start_row == e->end_row) {
1655 int t = e->line_type();
1656 switch (t) {
1657 case -1:
1658 had_non_line = 1;
1659 break;
1660 case 0:
1661 // empty
1662 break;
1663 case 1:
1664 had_single = 1;
1665 break;
1666 case 2:
1667 had_double = 1;
1668 break;
1669 default:
1670 assert(0);
1672 if (had_non_line)
1673 break;
1675 c = e->end_col;
1678 if (had_non_line)
1679 row_is_all_lines[i] = 0;
1680 else if (had_double)
1681 row_is_all_lines[i] = 2;
1682 else if (had_single)
1683 row_is_all_lines[i] = 1;
1684 else
1685 row_is_all_lines[i] = 0;
1689 void table::init_output()
1691 prints(".nr " COMPATIBLE_REG " \\n(.C\n"
1692 ".cp 0\n");
1693 if (linesize > 0)
1694 printfs(".nr " LINESIZE_REG " %1\n", as_string(linesize));
1695 else
1696 prints(".nr " LINESIZE_REG " \\n[.s]\n");
1697 if (!(flags & CENTER))
1698 prints(".nr " SAVED_CENTER_REG " \\n[.ce]\n");
1699 if (compatible_flag)
1700 prints(".ds " LEADER_REG " \\a\n");
1701 prints(".de " RESET_MACRO_NAME "\n"
1702 ".ft \\n[.f]\n"
1703 ".ps \\n[.s]\n"
1704 ".vs \\n[.v]u\n"
1705 ".in \\n[.i]u\n"
1706 ".ll \\n[.l]u\n"
1707 ".ls \\n[.L]\n"
1708 ".ad \\n[.j]\n"
1709 ".ie \\n[.u] .fi\n"
1710 ".el .nf\n"
1711 ".ce \\n[.ce]\n"
1712 "..\n"
1713 ".nr " SAVED_INDENT_REG " \\n[.i]\n"
1714 ".nr " SAVED_FONT_REG " \\n[.f]\n"
1715 ".nr " SAVED_SIZE_REG " \\n[.s]\n"
1716 ".nr " SAVED_FILL_REG " \\n[.u]\n"
1717 ".nr T. 0\n"
1718 ".nr " CURRENT_ROW_REG " 0-1\n"
1719 ".nr " LAST_PASSED_ROW_REG " 0-1\n"
1720 ".nr " SECTION_DIVERSION_FLAG_REG " 0\n"
1721 ".ds " TRANSPARENT_STRING_NAME "\n"
1722 ".ds " QUOTE_STRING_NAME "\n"
1723 ".nr " NEED_BOTTOM_RULE_REG " 1\n"
1724 ".nr " SUPPRESS_BOTTOM_REG " 0\n"
1725 ".eo\n"
1726 ".de " REPEATED_MARK_MACRO "\n"
1727 ".mk \\$1\n"
1728 ".if !'\\n(.z'' \\!." REPEATED_MARK_MACRO " \"\\$1\"\n"
1729 "..\n"
1730 ".de " REPEATED_VPT_MACRO "\n"
1731 ".vpt \\$1\n"
1732 ".if !'\\n(.z'' \\!." REPEATED_VPT_MACRO " \"\\$1\"\n"
1733 "..\n");
1734 if (!(flags & NOKEEP))
1735 prints(".de " KEEP_MACRO_NAME "\n"
1736 ".if '\\n[.z]'' \\{.ds " QUOTE_STRING_NAME " \\\\\n"
1737 ".ds " TRANSPARENT_STRING_NAME " \\!\n"
1738 ".di " SECTION_DIVERSION_NAME "\n"
1739 ".nr " SECTION_DIVERSION_FLAG_REG " 1\n"
1740 ".in 0\n"
1741 ".\\}\n"
1742 "..\n"
1743 ".de " RELEASE_MACRO_NAME "\n"
1744 ".if \\n[" SECTION_DIVERSION_FLAG_REG "] \\{"
1745 ".di\n"
1746 ".in \\n[" SAVED_INDENT_REG "]u\n"
1747 ".nr " SAVED_DN_REG " \\n[dn]\n"
1748 ".ds " QUOTE_STRING_NAME "\n"
1749 ".ds " TRANSPARENT_STRING_NAME "\n"
1750 ".nr " SECTION_DIVERSION_FLAG_REG " 0\n"
1751 ".if \\n[.t]<=\\n[dn] \\{"
1752 ".nr T. 1\n"
1753 ".T#\n"
1754 ".nr " SUPPRESS_BOTTOM_REG " 1\n"
1755 ".sp \\n[.t]u\n"
1756 ".nr " SUPPRESS_BOTTOM_REG " 0\n"
1757 ".mk #T\n"
1758 ".\\}\n"
1759 ".if \\n[.t]<=\\n[" SAVED_DN_REG "] "
1760 /* Since we turn off traps, it won't get into an infinite loop
1761 when we try and print it; it will just go off the bottom of the
1762 page. */
1763 ".tm warning: page \\n%: table text block will not fit on one page\n"
1764 ".nf\n"
1765 ".ls 1\n"
1766 "." SECTION_DIVERSION_NAME "\n"
1767 ".ls\n"
1768 ".rm " SECTION_DIVERSION_NAME "\n"
1769 ".\\}\n"
1770 "..\n"
1771 ".nr " TABLE_DIVERSION_FLAG_REG " 0\n"
1772 ".de " TABLE_KEEP_MACRO_NAME "\n"
1773 ".if '\\n[.z]'' \\{"
1774 ".di " TABLE_DIVERSION_NAME "\n"
1775 ".nr " TABLE_DIVERSION_FLAG_REG " 1\n"
1776 ".\\}\n"
1777 "..\n"
1778 ".de " TABLE_RELEASE_MACRO_NAME "\n"
1779 ".if \\n[" TABLE_DIVERSION_FLAG_REG "] \\{.br\n"
1780 ".di\n"
1781 ".nr " SAVED_DN_REG " \\n[dn]\n"
1782 ".ne \\n[dn]u+\\n[.V]u\n"
1783 ".ie \\n[.t]<=\\n[" SAVED_DN_REG "] "
1784 ".tm error: page \\n%: table will not fit on one page; use .TS H/.TH with a supporting macro package\n"
1785 ".el \\{"
1786 ".in 0\n"
1787 ".ls 1\n"
1788 ".nf\n"
1789 "." TABLE_DIVERSION_NAME "\n"
1790 ".\\}\n"
1791 ".rm " TABLE_DIVERSION_NAME "\n"
1792 ".\\}\n"
1793 "..\n");
1794 prints(".ec\n"
1795 ".ce 0\n"
1796 ".nf\n");
1799 string block_width_reg(int r, int c)
1801 static char name[sizeof(BLOCK_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1802 sprintf(name, BLOCK_WIDTH_PREFIX "%d,%d", r, c);
1803 return string(name);
1806 string block_diversion_name(int r, int c)
1808 static char name[sizeof(BLOCK_DIVERSION_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1809 sprintf(name, BLOCK_DIVERSION_PREFIX "%d,%d", r, c);
1810 return string(name);
1813 string block_height_reg(int r, int c)
1815 static char name[sizeof(BLOCK_HEIGHT_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1816 sprintf(name, BLOCK_HEIGHT_PREFIX "%d,%d", r, c);
1817 return string(name);
1820 string span_width_reg(int start_col, int end_col)
1822 static char name[sizeof(SPAN_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1823 sprintf(name, SPAN_WIDTH_PREFIX "%d", start_col);
1824 if (end_col != start_col)
1825 sprintf(strchr(name, '\0'), ",%d", end_col);
1826 return string(name);
1829 string span_left_numeric_width_reg(int start_col, int end_col)
1831 static char name[sizeof(SPAN_LEFT_NUMERIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1832 sprintf(name, SPAN_LEFT_NUMERIC_WIDTH_PREFIX "%d", start_col);
1833 if (end_col != start_col)
1834 sprintf(strchr(name, '\0'), ",%d", end_col);
1835 return string(name);
1838 string span_right_numeric_width_reg(int start_col, int end_col)
1840 static char name[sizeof(SPAN_RIGHT_NUMERIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1841 sprintf(name, SPAN_RIGHT_NUMERIC_WIDTH_PREFIX "%d", start_col);
1842 if (end_col != start_col)
1843 sprintf(strchr(name, '\0'), ",%d", end_col);
1844 return string(name);
1847 string span_alphabetic_width_reg(int start_col, int end_col)
1849 static char name[sizeof(SPAN_ALPHABETIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1850 sprintf(name, SPAN_ALPHABETIC_WIDTH_PREFIX "%d", start_col);
1851 if (end_col != start_col)
1852 sprintf(strchr(name, '\0'), ",%d", end_col);
1853 return string(name);
1856 string column_separation_reg(int col)
1858 static char name[sizeof(COLUMN_SEPARATION_PREFIX)+INT_DIGITS];
1859 sprintf(name, COLUMN_SEPARATION_PREFIX "%d", col);
1860 return string(name);
1863 string row_start_reg(int row)
1865 static char name[sizeof(ROW_START_PREFIX)+INT_DIGITS];
1866 sprintf(name, ROW_START_PREFIX "%d", row);
1867 return string(name);
1870 string column_start_reg(int col)
1872 static char name[sizeof(COLUMN_START_PREFIX)+INT_DIGITS];
1873 sprintf(name, COLUMN_START_PREFIX "%d", col);
1874 return string(name);
1877 string column_end_reg(int col)
1879 static char name[sizeof(COLUMN_END_PREFIX)+INT_DIGITS];
1880 sprintf(name, COLUMN_END_PREFIX "%d", col);
1881 return string(name);
1884 string column_divide_reg(int col)
1886 static char name[sizeof(COLUMN_DIVIDE_PREFIX)+INT_DIGITS];
1887 sprintf(name, COLUMN_DIVIDE_PREFIX "%d", col);
1888 return string(name);
1891 string row_top_reg(int row)
1893 static char name[sizeof(ROW_TOP_PREFIX)+INT_DIGITS];
1894 sprintf(name, ROW_TOP_PREFIX "%d", row);
1895 return string(name);
1898 void init_span_reg(int start_col, int end_col)
1900 printfs(".nr %1 \\n(.H\n.nr %2 0\n.nr %3 0\n.nr %4 0\n",
1901 span_width_reg(start_col, end_col),
1902 span_alphabetic_width_reg(start_col, end_col),
1903 span_left_numeric_width_reg(start_col, end_col),
1904 span_right_numeric_width_reg(start_col, end_col));
1907 void compute_span_width(int start_col, int end_col)
1909 printfs(".nr %1 \\n[%1]>?(\\n[%2]+\\n[%3])\n"
1910 ".if \\n[%4] .nr %1 \\n[%1]>?(\\n[%4]+2n)\n",
1911 span_width_reg(start_col, end_col),
1912 span_left_numeric_width_reg(start_col, end_col),
1913 span_right_numeric_width_reg(start_col, end_col),
1914 span_alphabetic_width_reg(start_col, end_col));
1917 // Increase the widths of columns so that the width of any spanning entry
1918 // is not greater than the sum of the widths of the columns that it spans.
1919 // Ensure that the widths of columns remain equal.
1921 void table::divide_span(int start_col, int end_col)
1923 assert(end_col > start_col);
1924 printfs(".nr " NEEDED_REG " \\n[%1]-(\\n[%2]",
1925 span_width_reg(start_col, end_col),
1926 span_width_reg(start_col, start_col));
1927 int i;
1928 for (i = start_col + 1; i <= end_col; i++) {
1929 // The column separation may shrink with the expand option.
1930 if (!(flags & EXPAND))
1931 printfs("+%1n", as_string(column_separation[i - 1]));
1932 printfs("+\\n[%1]", span_width_reg(i, i));
1934 prints(")\n");
1935 printfs(".nr " NEEDED_REG " \\n[" NEEDED_REG "]/%1\n",
1936 as_string(end_col - start_col + 1));
1937 prints(".if \\n[" NEEDED_REG "] \\{");
1938 for (i = start_col; i <= end_col; i++)
1939 printfs(".nr %1 +\\n[" NEEDED_REG "]\n",
1940 span_width_reg(i, i));
1941 int equal_flag = 0;
1942 for (i = start_col; i <= end_col && !equal_flag; i++)
1943 if (equal[i])
1944 equal_flag = 1;
1945 if (equal_flag) {
1946 for (i = 0; i < ncolumns; i++)
1947 if (i < start_col || i > end_col)
1948 printfs(".nr %1 +\\n[" NEEDED_REG "]\n",
1949 span_width_reg(i, i));
1951 prints(".\\}\n");
1954 void table::sum_columns(int start_col, int end_col)
1956 assert(end_col > start_col);
1957 printfs(".nr %1 \\n[%2]",
1958 span_width_reg(start_col, end_col),
1959 span_width_reg(start_col, start_col));
1960 for (int i = start_col + 1; i <= end_col; i++)
1961 printfs("+(%1*\\n[" SEPARATION_FACTOR_REG "])+\\n[%2]",
1962 as_string(column_separation[i - 1]),
1963 span_width_reg(i, i));
1964 prints('\n');
1967 horizontal_span::horizontal_span(int sc, int ec, horizontal_span *p)
1968 : next(p), start_col(sc), end_col(ec)
1972 void table::build_span_list()
1974 span_list = 0;
1975 table_entry *p = entry_list;
1976 while (p) {
1977 if (p->end_col != p->start_col) {
1978 horizontal_span *q;
1979 for (q = span_list; q; q = q->next)
1980 if (q->start_col == p->start_col
1981 && q->end_col == p->end_col)
1982 break;
1983 if (!q)
1984 span_list = new horizontal_span(p->start_col, p->end_col, span_list);
1986 p = p->next;
1988 // Now sort span_list primarily by order of end_row, and secondarily
1989 // by reverse order of start_row. This ensures that if we divide
1990 // spans using the order in span_list, we will get reasonable results.
1991 horizontal_span *unsorted = span_list;
1992 span_list = 0;
1993 while (unsorted) {
1994 horizontal_span **pp;
1995 for (pp = &span_list; *pp; pp = &(*pp)->next)
1996 if (unsorted->end_col < (*pp)->end_col
1997 || (unsorted->end_col == (*pp)->end_col
1998 && (unsorted->start_col > (*pp)->start_col)))
1999 break;
2000 horizontal_span *tem = unsorted->next;
2001 unsorted->next = *pp;
2002 *pp = unsorted;
2003 unsorted = tem;
2007 void table::compute_separation_factor()
2009 if (flags & (ALLBOX|BOX|DOUBLEBOX))
2010 left_separation = right_separation = 1;
2011 else {
2012 for (int i = 0; i < nrows; i++) {
2013 if (vline[i][0] > 0)
2014 left_separation = 1;
2015 if (vline[i][ncolumns] > 0)
2016 right_separation = 1;
2019 if (flags & EXPAND) {
2020 int total_sep = left_separation + right_separation;
2021 int i;
2022 for (i = 0; i < ncolumns - 1; i++)
2023 total_sep += column_separation[i];
2024 if (total_sep != 0) {
2025 // Don't let the separation factor be negative.
2026 prints(".nr " SEPARATION_FACTOR_REG " \\n[.l]-\\n[.i]");
2027 for (i = 0; i < ncolumns; i++)
2028 printfs("-\\n[%1]", span_width_reg(i, i));
2029 printfs("/%1>?0\n", as_string(total_sep));
2034 void table::compute_column_positions()
2036 printfs(".nr %1 0\n", column_divide_reg(0));
2037 printfs(".nr %1 %2*\\n[" SEPARATION_FACTOR_REG "]\n",
2038 column_start_reg(0),
2039 as_string(left_separation));
2040 int i;
2041 for (i = 1;; i++) {
2042 printfs(".nr %1 \\n[%2]+\\n[%3]\n",
2043 column_end_reg(i-1),
2044 column_start_reg(i-1),
2045 span_width_reg(i-1, i-1));
2046 if (i >= ncolumns)
2047 break;
2048 printfs(".nr %1 \\n[%2]+(%3*\\n[" SEPARATION_FACTOR_REG "])\n",
2049 column_start_reg(i),
2050 column_end_reg(i-1),
2051 as_string(column_separation[i-1]));
2052 printfs(".nr %1 \\n[%2]+\\n[%3]/2\n",
2053 column_divide_reg(i),
2054 column_end_reg(i-1),
2055 column_start_reg(i));
2057 printfs(".nr %1 \\n[%2]+(%3*\\n[" SEPARATION_FACTOR_REG "])\n",
2058 column_divide_reg(ncolumns),
2059 column_end_reg(i-1),
2060 as_string(right_separation));
2061 printfs(".nr TW \\n[%1]\n",
2062 column_divide_reg(ncolumns));
2063 if (flags & DOUBLEBOX) {
2064 printfs(".nr %1 +" DOUBLE_LINE_SEP "\n", column_divide_reg(0));
2065 printfs(".nr %1 -" DOUBLE_LINE_SEP "\n", column_divide_reg(ncolumns));
2069 void table::make_columns_equal()
2071 int first = -1; // index of first equal column
2072 int i;
2073 for (i = 0; i < ncolumns; i++)
2074 if (equal[i]) {
2075 if (first < 0) {
2076 printfs(".nr %1 \\n[%1]", span_width_reg(i, i));
2077 first = i;
2079 else
2080 printfs(">?\\n[%1]", span_width_reg(i, i));
2082 if (first >= 0) {
2083 prints('\n');
2084 for (i = first + 1; i < ncolumns; i++)
2085 if (equal[i])
2086 printfs(".nr %1 \\n[%2]\n",
2087 span_width_reg(i, i),
2088 span_width_reg(first, first));
2092 void table::compute_widths()
2094 build_span_list();
2095 int i;
2096 horizontal_span *p;
2097 prints(".nr " SEPARATION_FACTOR_REG " 1n\n");
2098 for (i = 0; i < ncolumns; i++) {
2099 init_span_reg(i, i);
2100 if (!minimum_width[i].empty())
2101 printfs(".nr %1 %2\n", span_width_reg(i, i), minimum_width[i]);
2103 for (p = span_list; p; p = p->next)
2104 init_span_reg(p->start_col, p->end_col);
2105 table_entry *q;
2106 for (q = entry_list; q; q = q->next)
2107 if (!q->mod->zero_width)
2108 q->do_width();
2109 for (i = 0; i < ncolumns; i++)
2110 compute_span_width(i, i);
2111 for (p = span_list; p; p = p->next)
2112 compute_span_width(p->start_col, p->end_col);
2113 make_columns_equal();
2114 // Note that divide_span keeps equal width columns equal.
2115 for (p = span_list; p; p = p->next)
2116 divide_span(p->start_col, p->end_col);
2117 for (p = span_list; p; p = p->next)
2118 sum_columns(p->start_col, p->end_col);
2119 int had_spanning_block = 0;
2120 int had_equal_block = 0;
2121 for (q = entry_list; q; q = q->next)
2122 if (q->divert(ncolumns, minimum_width,
2123 (flags & EXPAND) ? column_separation : 0)) {
2124 if (q->end_col > q->start_col)
2125 had_spanning_block = 1;
2126 for (i = q->start_col; i <= q->end_col && !had_equal_block; i++)
2127 if (equal[i])
2128 had_equal_block = 1;
2130 if (had_equal_block)
2131 make_columns_equal();
2132 if (had_spanning_block)
2133 for (p = span_list; p; p = p->next)
2134 divide_span(p->start_col, p->end_col);
2135 compute_separation_factor();
2136 for (p = span_list; p; p = p->next)
2137 sum_columns(p->start_col, p->end_col);
2138 compute_column_positions();
2141 void table::print_single_hline(int r)
2143 prints(".vs " LINE_SEP ">?\\n[.V]u\n"
2144 ".ls 1\n"
2145 "\\v'" BODY_DEPTH "'"
2146 "\\s[\\n[" LINESIZE_REG "]]");
2147 if (r > nrows - 1)
2148 prints("\\D'l |\\n[TW]u 0'");
2149 else {
2150 int start_col = 0;
2151 for (;;) {
2152 while (start_col < ncolumns
2153 && entry[r][start_col] != 0
2154 && entry[r][start_col]->start_row != r)
2155 start_col++;
2156 int end_col;
2157 for (end_col = start_col;
2158 end_col < ncolumns
2159 && (entry[r][end_col] == 0
2160 || entry[r][end_col]->start_row == r);
2161 end_col++)
2163 if (end_col <= start_col)
2164 break;
2165 printfs("\\h'|\\n[%1]u",
2166 column_divide_reg(start_col));
2167 if ((r > 0 && vline[r-1][start_col] == 2)
2168 || (r < nrows && vline[r][start_col] == 2))
2169 prints("-" HALF_DOUBLE_LINE_SEP);
2170 prints("'");
2171 printfs("\\D'l |\\n[%1]u",
2172 column_divide_reg(end_col));
2173 if ((r > 0 && vline[r-1][end_col] == 2)
2174 || (r < nrows && vline[r][end_col] == 2))
2175 prints("+" HALF_DOUBLE_LINE_SEP);
2176 prints(" 0'");
2177 start_col = end_col;
2180 prints("\\s0\n");
2181 prints(".ls\n"
2182 ".vs\n");
2185 void table::print_double_hline(int r)
2187 prints(".vs " LINE_SEP "+" DOUBLE_LINE_SEP
2188 ">?\\n[.V]u\n"
2189 ".ls 1\n"
2190 "\\v'" BODY_DEPTH "'"
2191 "\\s[\\n[" LINESIZE_REG "]]");
2192 if (r > nrows - 1)
2193 prints("\\v'-" DOUBLE_LINE_SEP "'"
2194 "\\D'l |\\n[TW]u 0'"
2195 "\\v'" DOUBLE_LINE_SEP "'"
2196 "\\h'|0'"
2197 "\\D'l |\\n[TW]u 0'");
2198 else {
2199 int start_col = 0;
2200 for (;;) {
2201 while (start_col < ncolumns
2202 && entry[r][start_col] != 0
2203 && entry[r][start_col]->start_row != r)
2204 start_col++;
2205 int end_col;
2206 for (end_col = start_col;
2207 end_col < ncolumns
2208 && (entry[r][end_col] == 0
2209 || entry[r][end_col]->start_row == r);
2210 end_col++)
2212 if (end_col <= start_col)
2213 break;
2214 const char *left_adjust = 0;
2215 if ((r > 0 && vline[r-1][start_col] == 2)
2216 || (r < nrows && vline[r][start_col] == 2))
2217 left_adjust = "-" HALF_DOUBLE_LINE_SEP;
2218 const char *right_adjust = 0;
2219 if ((r > 0 && vline[r-1][end_col] == 2)
2220 || (r < nrows && vline[r][end_col] == 2))
2221 right_adjust = "+" HALF_DOUBLE_LINE_SEP;
2222 printfs("\\v'-" DOUBLE_LINE_SEP "'"
2223 "\\h'|\\n[%1]u",
2224 column_divide_reg(start_col));
2225 if (left_adjust)
2226 prints(left_adjust);
2227 prints("'");
2228 printfs("\\D'l |\\n[%1]u",
2229 column_divide_reg(end_col));
2230 if (right_adjust)
2231 prints(right_adjust);
2232 prints(" 0'");
2233 printfs("\\v'" DOUBLE_LINE_SEP "'"
2234 "\\h'|\\n[%1]u",
2235 column_divide_reg(start_col));
2236 if (left_adjust)
2237 prints(left_adjust);
2238 prints("'");
2239 printfs("\\D'l |\\n[%1]u",
2240 column_divide_reg(end_col));
2241 if (right_adjust)
2242 prints(right_adjust);
2243 prints(" 0'");
2244 start_col = end_col;
2247 prints("\\s0\n"
2248 ".ls\n"
2249 ".vs\n");
2252 void table::compute_vrule_top_adjust(int start_row, int col, string &result)
2254 if (row_is_all_lines[start_row] && start_row < nrows - 1) {
2255 if (row_is_all_lines[start_row] == 2)
2256 result = LINE_SEP ">?\\n[.V]u" "+" DOUBLE_LINE_SEP;
2257 else
2258 result = LINE_SEP ">?\\n[.V]u";
2259 start_row++;
2261 else {
2262 result = "";
2263 if (start_row == 0)
2264 return;
2265 for (stuff *p = stuff_list; p && p->row <= start_row; p = p->next)
2266 if (p->row == start_row
2267 && (p->is_single_line() || p->is_double_line()))
2268 return;
2270 int left = 0;
2271 if (col > 0) {
2272 table_entry *e = entry[start_row-1][col-1];
2273 if (e && e->start_row == e->end_row) {
2274 if (e->to_double_line_entry() != 0)
2275 left = 2;
2276 else if (e->to_single_line_entry() != 0)
2277 left = 1;
2280 int right = 0;
2281 if (col < ncolumns) {
2282 table_entry *e = entry[start_row-1][col];
2283 if (e && e->start_row == e->end_row) {
2284 if (e->to_double_line_entry() != 0)
2285 right = 2;
2286 else if (e->to_single_line_entry() != 0)
2287 right = 1;
2290 if (row_is_all_lines[start_row-1] == 0) {
2291 if (left > 0 || right > 0) {
2292 result += "-" BODY_DEPTH "-" BAR_HEIGHT;
2293 if ((left == 2 && right != 2) || (right == 2 && left != 2))
2294 result += "-" HALF_DOUBLE_LINE_SEP;
2295 else if (left == 2 && right == 2)
2296 result += "+" HALF_DOUBLE_LINE_SEP;
2299 else if (row_is_all_lines[start_row-1] == 2) {
2300 if ((left == 2 && right != 2) || (right == 2 && left != 2))
2301 result += "-" DOUBLE_LINE_SEP;
2302 else if (left == 1 || right == 1)
2303 result += "-" HALF_DOUBLE_LINE_SEP;
2307 void table::compute_vrule_bot_adjust(int end_row, int col, string &result)
2309 if (row_is_all_lines[end_row] && end_row > 0) {
2310 end_row--;
2311 result = "";
2313 else {
2314 stuff *p;
2315 for (p = stuff_list; p && p->row < end_row + 1; p = p->next)
2317 if (p && p->row == end_row + 1 && p->is_double_line()) {
2318 result = "-" DOUBLE_LINE_SEP;
2319 return;
2321 if ((p != 0 && p->row == end_row + 1)
2322 || end_row == nrows - 1) {
2323 result = "";
2324 return;
2326 if (row_is_all_lines[end_row+1] == 1)
2327 result = LINE_SEP;
2328 else if (row_is_all_lines[end_row+1] == 2)
2329 result = LINE_SEP "+" DOUBLE_LINE_SEP;
2330 else
2331 result = "";
2333 int left = 0;
2334 if (col > 0) {
2335 table_entry *e = entry[end_row+1][col-1];
2336 if (e && e->start_row == e->end_row) {
2337 if (e->to_double_line_entry() != 0)
2338 left = 2;
2339 else if (e->to_single_line_entry() != 0)
2340 left = 1;
2343 int right = 0;
2344 if (col < ncolumns) {
2345 table_entry *e = entry[end_row+1][col];
2346 if (e && e->start_row == e->end_row) {
2347 if (e->to_double_line_entry() != 0)
2348 right = 2;
2349 else if (e->to_single_line_entry() != 0)
2350 right = 1;
2353 if (row_is_all_lines[end_row+1] == 0) {
2354 if (left > 0 || right > 0) {
2355 result = "1v-" BODY_DEPTH "-" BAR_HEIGHT;
2356 if ((left == 2 && right != 2) || (right == 2 && left != 2))
2357 result += "+" HALF_DOUBLE_LINE_SEP;
2358 else if (left == 2 && right == 2)
2359 result += "-" HALF_DOUBLE_LINE_SEP;
2362 else if (row_is_all_lines[end_row+1] == 2) {
2363 if (left == 2 && right == 2)
2364 result += "-" DOUBLE_LINE_SEP;
2365 else if (left != 2 && right != 2 && (left == 1 || right == 1))
2366 result += "-" HALF_DOUBLE_LINE_SEP;
2370 void table::add_vertical_rule(int start_row, int end_row, int col, int is_double)
2372 vrule_list = new vertical_rule(start_row, end_row, col, is_double,
2373 vrule_list);
2374 compute_vrule_top_adjust(start_row, col, vrule_list->top_adjust);
2375 compute_vrule_bot_adjust(end_row, col, vrule_list->bot_adjust);
2378 void table::build_vrule_list()
2380 int col;
2381 if (flags & ALLBOX) {
2382 for (col = 1; col < ncolumns; col++) {
2383 int start_row = 0;
2384 for (;;) {
2385 while (start_row < nrows && vline_spanned(start_row, col))
2386 start_row++;
2387 if (start_row >= nrows)
2388 break;
2389 int end_row = start_row;
2390 while (end_row < nrows && !vline_spanned(end_row, col))
2391 end_row++;
2392 end_row--;
2393 add_vertical_rule(start_row, end_row, col, 0);
2394 start_row = end_row + 1;
2398 if (flags & (BOX|ALLBOX|DOUBLEBOX)) {
2399 add_vertical_rule(0, nrows - 1, 0, 0);
2400 add_vertical_rule(0, nrows - 1, ncolumns, 0);
2402 for (int end_row = 0; end_row < nrows; end_row++)
2403 for (col = 0; col < ncolumns+1; col++)
2404 if (vline[end_row][col] > 0
2405 && !vline_spanned(end_row, col)
2406 && (end_row == nrows - 1
2407 || vline[end_row+1][col] != vline[end_row][col]
2408 || vline_spanned(end_row+1, col))) {
2409 int start_row;
2410 for (start_row = end_row - 1;
2411 start_row >= 0
2412 && vline[start_row][col] == vline[end_row][col]
2413 && !vline_spanned(start_row, col);
2414 start_row--)
2416 start_row++;
2417 add_vertical_rule(start_row, end_row, col, vline[end_row][col] > 1);
2419 for (vertical_rule *p = vrule_list; p; p = p->next)
2420 if (p->is_double)
2421 for (int r = p->start_row; r <= p->end_row; r++) {
2422 if (p->col > 0 && entry[r][p->col-1] != 0
2423 && entry[r][p->col-1]->end_col == p->col-1) {
2424 int is_corner = r == p->start_row || r == p->end_row;
2425 entry[r][p->col-1]->note_double_vrule_on_right(is_corner);
2427 if (p->col < ncolumns && entry[r][p->col] != 0
2428 && entry[r][p->col]->start_col == p->col) {
2429 int is_corner = r == p->start_row || r == p->end_row;
2430 entry[r][p->col]->note_double_vrule_on_left(is_corner);
2435 void table::define_bottom_macro()
2437 prints(".eo\n"
2438 ".de T#\n"
2439 ".if !\\n[" SUPPRESS_BOTTOM_REG "] \\{"
2440 "." REPEATED_VPT_MACRO " 0\n"
2441 ".mk " SAVED_VERTICAL_POS_REG "\n");
2442 if (flags & (BOX|ALLBOX|DOUBLEBOX)) {
2443 prints(".if \\n[T.]&\\n[" NEED_BOTTOM_RULE_REG "] \\{");
2444 print_single_hline(0);
2445 prints(".\\}\n");
2447 prints(".ls 1\n");
2448 for (vertical_rule *p = vrule_list; p; p = p->next)
2449 p->contribute_to_bottom_macro(this);
2450 if (flags & DOUBLEBOX)
2451 prints(".if \\n[T.] \\{.vs " DOUBLE_LINE_SEP ">?\\n[.V]u\n"
2452 "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]"
2453 "\\D'l \\n[TW]u 0'\\s0\n"
2454 ".vs\n"
2455 ".\\}\n"
2456 ".if \\n[" LAST_PASSED_ROW_REG "]>=0 "
2457 ".nr " TOP_REG " \\n[#T]-" DOUBLE_LINE_SEP "\n"
2458 ".sp -1\n"
2459 "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]"
2460 "\\D'l 0 |\\n[" TOP_REG "]u-1v'\\s0\n"
2461 ".sp -1\n"
2462 "\\v'" BODY_DEPTH "'\\h'|\\n[TW]u'\\s[\\n[" LINESIZE_REG "]]"
2463 "\\D'l 0 |\\n[" TOP_REG "]u-1v'\\s0\n");
2464 prints(".ls\n");
2465 prints(".nr " LAST_PASSED_ROW_REG " \\n[" CURRENT_ROW_REG "]\n"
2466 ".sp |\\n[" SAVED_VERTICAL_POS_REG "]u\n"
2467 "." REPEATED_VPT_MACRO " 1\n"
2468 ".\\}\n"
2469 "..\n"
2470 ".ec\n");
2473 // is the vertical line before column c in row r horizontally spanned?
2475 int table::vline_spanned(int r, int c)
2477 assert(r >= 0 && r < nrows && c >= 0 && c < ncolumns + 1);
2478 return (c != 0 && c != ncolumns && entry[r][c] != 0
2479 && entry[r][c]->start_col != c
2480 // horizontally spanning lines don't count
2481 && entry[r][c]->to_double_line_entry() == 0
2482 && entry[r][c]->to_single_line_entry() == 0);
2485 int table::row_begins_section(int r)
2487 assert(r >= 0 && r < nrows);
2488 for (int i = 0; i < ncolumns; i++)
2489 if (entry[r][i] && entry[r][i]->start_row != r)
2490 return 0;
2491 return 1;
2494 int table::row_ends_section(int r)
2496 assert(r >= 0 && r < nrows);
2497 for (int i = 0; i < ncolumns; i++)
2498 if (entry[r][i] && entry[r][i]->end_row != r)
2499 return 0;
2500 return 1;
2503 void table::do_row(int r)
2505 if (!(flags & NOKEEP) && row_begins_section(r))
2506 prints("." KEEP_MACRO_NAME "\n");
2507 int had_line = 0;
2508 stuff *p;
2509 for (p = stuff_list; p && p->row < r; p = p->next)
2511 for (stuff *p1 = p; p1 && p1->row == r; p1 = p1->next)
2512 if (!p1->printed && (p1->is_single_line() || p1->is_double_line())) {
2513 had_line = 1;
2514 break;
2516 if (!had_line && !row_is_all_lines[r])
2517 printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r));
2518 had_line = 0;
2519 for (; p && p->row == r; p = p->next)
2520 if (!p->printed) {
2521 p->print(this);
2522 if (!had_line && (p->is_single_line() || p->is_double_line())) {
2523 printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r));
2524 had_line = 1;
2527 // Change the row *after* printing the stuff list (which might contain .TH).
2528 printfs("\\*[" TRANSPARENT_STRING_NAME "].nr " CURRENT_ROW_REG " %1\n",
2529 as_string(r));
2530 if (!had_line && row_is_all_lines[r])
2531 printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r));
2532 // we might have had a .TH, for example, since we last tried
2533 if (!(flags & NOKEEP) && row_begins_section(r))
2534 prints("." KEEP_MACRO_NAME "\n");
2535 printfs(".mk %1\n", row_start_reg(r));
2536 prints(".mk " BOTTOM_REG "\n"
2537 "." REPEATED_VPT_MACRO " 0\n");
2538 int c;
2539 int row_is_blank = 1;
2540 int first_start_row = r;
2541 for (c = 0; c < ncolumns; c++) {
2542 table_entry *e = entry[r][c];
2543 if (e) {
2544 if (e->end_row == r) {
2545 e->do_depth();
2546 if (e->start_row < first_start_row)
2547 first_start_row = e->start_row;
2548 row_is_blank = 0;
2550 c = e->end_col;
2553 if (row_is_blank)
2554 prints(".nr " BOTTOM_REG " +1v\n");
2555 if (row_is_all_lines[r]) {
2556 prints(".vs " LINE_SEP);
2557 if (row_is_all_lines[r] == 2)
2558 prints("+" DOUBLE_LINE_SEP);
2559 prints(">?\\n[.V]u\n.ls 1\n");
2560 prints("\\&");
2561 prints("\\v'" BODY_DEPTH);
2562 if (row_is_all_lines[r] == 2)
2563 prints("-" HALF_DOUBLE_LINE_SEP);
2564 prints("'");
2565 for (c = 0; c < ncolumns; c++) {
2566 table_entry *e = entry[r][c];
2567 if (e) {
2568 if (e->end_row == e->start_row)
2569 e->to_simple_entry()->simple_print(1);
2570 c = e->end_col;
2573 prints("\n");
2574 prints(".ls\n"
2575 ".vs\n");
2576 prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n");
2577 printfs(".sp |\\n[%1]u\n", row_start_reg(r));
2579 for (int i = row_is_all_lines[r] ? r - 1 : r;
2580 i >= first_start_row;
2581 i--) {
2582 simple_entry *first = 0;
2583 for (c = 0; c < ncolumns; c++) {
2584 table_entry *e = entry[r][c];
2585 if (e) {
2586 if (e->end_row == r && e->start_row == i) {
2587 simple_entry *simple = e->to_simple_entry();
2588 if (simple) {
2589 if (!first) {
2590 prints(".ta");
2591 first = simple;
2593 simple->add_tab();
2596 c = e->end_col;
2599 if (first) {
2600 prints('\n');
2601 first->position_vertically();
2602 first->set_location();
2603 prints("\\&");
2604 first->simple_print(0);
2605 for (c = first->end_col + 1; c < ncolumns; c++) {
2606 table_entry *e = entry[r][c];
2607 if (e) {
2608 if (e->end_row == r && e->start_row == i) {
2609 simple_entry *simple = e->to_simple_entry();
2610 if (simple) {
2611 if (e->end_row != e->start_row) {
2612 prints('\n');
2613 simple->position_vertically();
2614 prints("\\&");
2616 simple->simple_print(0);
2619 c = e->end_col;
2622 prints('\n');
2623 prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n");
2624 printfs(".sp |\\n[%1]u\n", row_start_reg(r));
2627 for (c = 0; c < ncolumns; c++) {
2628 table_entry *e = entry[r][c];
2629 if (e) {
2630 if (e->end_row == r && e->to_simple_entry() == 0) {
2631 e->position_vertically();
2632 e->print();
2633 prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n");
2634 printfs(".sp |\\n[%1]u\n", row_start_reg(r));
2636 c = e->end_col;
2639 prints("." REPEATED_VPT_MACRO " 1\n"
2640 ".sp |\\n[" BOTTOM_REG "]u\n"
2641 "\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG " 1\n");
2642 if (r != nrows - 1 && (flags & ALLBOX)) {
2643 print_single_hline(r + 1);
2644 prints("\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG " 0\n");
2646 if (r != nrows - 1) {
2647 if (p && p->row == r + 1
2648 && (p->is_single_line() || p->is_double_line())) {
2649 p->print(this);
2650 prints("\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG
2651 " 0\n");
2653 int printed_one = 0;
2654 for (vertical_rule *vr = vrule_list; vr; vr = vr->next)
2655 if (vr->end_row == r) {
2656 if (!printed_one) {
2657 prints("." REPEATED_VPT_MACRO " 0\n");
2658 printed_one = 1;
2660 vr->print();
2662 if (printed_one)
2663 prints("." REPEATED_VPT_MACRO " 1\n");
2664 if (!(flags & NOKEEP) && row_ends_section(r))
2665 prints("." RELEASE_MACRO_NAME "\n");
2669 void table::do_top()
2671 prints(".fc \002\003\n");
2672 if (!(flags & NOKEEP) && (flags & (BOX|DOUBLEBOX|ALLBOX)))
2673 prints("." TABLE_KEEP_MACRO_NAME "\n");
2674 if (flags & DOUBLEBOX) {
2675 prints(".ls 1\n"
2676 ".vs " LINE_SEP ">?\\n[.V]u\n"
2677 "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]\\D'l \\n[TW]u 0'\\s0\n"
2678 ".vs\n"
2679 "." REPEATED_MARK_MACRO " " TOP_REG "\n"
2680 ".vs " DOUBLE_LINE_SEP ">?\\n[.V]u\n");
2681 printfs("\\v'" BODY_DEPTH "'"
2682 "\\s[\\n[" LINESIZE_REG "]]"
2683 "\\h'\\n[%1]u'"
2684 "\\D'l |\\n[%2]u 0'"
2685 "\\s0"
2686 "\n",
2687 column_divide_reg(0),
2688 column_divide_reg(ncolumns));
2689 prints(".ls\n"
2690 ".vs\n");
2692 else if (flags & (ALLBOX|BOX)) {
2693 print_single_hline(0);
2695 //printfs(".mk %1\n", row_top_reg(0));
2698 void table::do_bottom()
2700 // print stuff after last row
2701 for (stuff *p = stuff_list; p; p = p->next)
2702 if (p->row > nrows - 1)
2703 p->print(this);
2704 if (!(flags & NOKEEP))
2705 prints("." RELEASE_MACRO_NAME "\n");
2706 printfs(".mk %1\n", row_top_reg(nrows));
2707 prints(".nr " NEED_BOTTOM_RULE_REG " 1\n"
2708 ".nr T. 1\n"
2709 ".T#\n");
2710 if (!(flags & NOKEEP) && (flags & (BOX|DOUBLEBOX|ALLBOX)))
2711 prints("." TABLE_RELEASE_MACRO_NAME "\n");
2712 if (flags & DOUBLEBOX)
2713 prints(".sp " DOUBLE_LINE_SEP "\n");
2714 prints("." RESET_MACRO_NAME "\n"
2715 ".fc\n"
2716 ".cp \\n(" COMPATIBLE_REG "\n");
2719 int table::get_nrows()
2721 return nrows;
2724 const char *last_filename = 0;
2726 void set_troff_location(const char *fn, int ln)
2728 if (!location_force_filename && last_filename != 0
2729 && strcmp(fn, last_filename) == 0)
2730 printfs(".lf %1\n", as_string(ln));
2731 else {
2732 printfs(".lf %1 %2\n", as_string(ln), fn);
2733 last_filename = fn;
2734 location_force_filename = 0;
2738 void printfs(const char *s, const string &arg1, const string &arg2,
2739 const string &arg3, const string &arg4, const string &arg5)
2741 if (s) {
2742 char c;
2743 while ((c = *s++) != '\0') {
2744 if (c == '%') {
2745 switch (*s++) {
2746 case '1':
2747 prints(arg1);
2748 break;
2749 case '2':
2750 prints(arg2);
2751 break;
2752 case '3':
2753 prints(arg3);
2754 break;
2755 case '4':
2756 prints(arg4);
2757 break;
2758 case '5':
2759 prints(arg5);
2760 break;
2761 case '6':
2762 case '7':
2763 case '8':
2764 case '9':
2765 break;
2766 case '%':
2767 prints('%');
2768 break;
2769 default:
2770 assert(0);
2773 else
2774 prints(c);