Adapt src/pre-soelim (src/preproc/soelim)
[s-roff.git] / src / preproc / tbl / table.cpp
bloba7ee5204db23417f608f896a7c2de8028e6bb8bb
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2003, 2004, 2007, 2008,
3 2009
4 Free Software Foundation, Inc.
5 Written by James Clark (jjc@jclark.com)
7 This file is part of groff.
9 groff is free software; you can redistribute it and/or modify it under
10 the terms of the GNU General Public License as published by the Free
11 Software Foundation; either version 2, or (at your option) any later
12 version.
14 groff is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 for more details.
19 You should have received a copy of the GNU General Public License along
20 with groff; see the file COPYING. If not, write to the Free Software
21 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
23 #include "table.h"
25 #define BAR_HEIGHT ".25m"
26 #define DOUBLE_LINE_SEP "2p"
27 #define HALF_DOUBLE_LINE_SEP "1p"
28 #define LINE_SEP "2p"
29 #define BODY_DEPTH ".25m"
31 const int DEFAULT_COLUMN_SEPARATION = 3;
33 #define DELIMITER_CHAR "\\[tbl]"
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 EXPAND_REG PREFIX "expand"
69 #define LEADER_REG PREFIX LEADER
71 #define BLOCK_WIDTH_PREFIX PREFIX "tbw"
72 #define BLOCK_DIVERSION_PREFIX PREFIX "tbd"
73 #define BLOCK_HEIGHT_PREFIX PREFIX "tbh"
74 #define SPAN_WIDTH_PREFIX PREFIX "w"
75 #define SPAN_LEFT_NUMERIC_WIDTH_PREFIX PREFIX "lnw"
76 #define SPAN_RIGHT_NUMERIC_WIDTH_PREFIX PREFIX "rnw"
77 #define SPAN_ALPHABETIC_WIDTH_PREFIX PREFIX "aw"
78 #define COLUMN_SEPARATION_PREFIX PREFIX "cs"
79 #define ROW_START_PREFIX PREFIX "rs"
80 #define COLUMN_START_PREFIX PREFIX "cl"
81 #define COLUMN_END_PREFIX PREFIX "ce"
82 #define COLUMN_DIVIDE_PREFIX PREFIX "cd"
83 #define ROW_TOP_PREFIX PREFIX "rt"
85 string block_width_reg(int, int);
86 string block_diversion_name(int, int);
87 string block_height_reg(int, int);
88 string span_width_reg(int, int);
89 string span_left_numeric_width_reg(int, int);
90 string span_right_numeric_width_reg(int, int);
91 string span_alphabetic_width_reg(int, int);
92 string column_separation_reg(int);
93 string row_start_reg(int);
94 string column_start_reg(int);
95 string column_end_reg(int);
96 string column_divide_reg(int);
97 string row_top_reg(int);
99 void set_inline_modifier(const entry_modifier *);
100 void restore_inline_modifier(const entry_modifier *);
101 void set_modifier(const entry_modifier *);
102 int find_decimal_point(const char *, char, const char *);
104 string an_empty_string;
105 int location_force_filename = 0;
107 void printfs(const char *,
108 const string &arg1 = an_empty_string,
109 const string &arg2 = an_empty_string,
110 const string &arg3 = an_empty_string,
111 const string &arg4 = an_empty_string,
112 const string &arg5 = an_empty_string);
114 void prints(const string &);
116 inline void prints(char c)
118 putchar(c);
121 inline void prints(const char *s)
123 fputs(s, stdout);
126 void prints(const string &s)
128 if (!s.empty())
129 fwrite(s.contents(), 1, s.length(), stdout);
132 struct horizontal_span {
133 horizontal_span *next;
134 int start_col;
135 int end_col;
136 horizontal_span(int, int, horizontal_span *);
139 class single_line_entry;
140 class double_line_entry;
141 class simple_entry;
143 class table_entry {
144 friend class table;
145 table_entry *next;
146 int input_lineno;
147 const char *input_filename;
148 protected:
149 int start_row;
150 int end_row;
151 int start_col;
152 int end_col;
153 const table *parent;
154 const entry_modifier *mod;
155 public:
156 void set_location();
157 table_entry(const table *, const entry_modifier *);
158 virtual ~table_entry();
159 virtual int divert(int, const string *, int *, int);
160 virtual void do_width();
161 virtual void do_depth();
162 virtual void print() = 0;
163 virtual void position_vertically() = 0;
164 virtual single_line_entry *to_single_line_entry();
165 virtual double_line_entry *to_double_line_entry();
166 virtual simple_entry *to_simple_entry();
167 virtual int line_type();
168 virtual void note_double_vrule_on_right(int);
169 virtual void note_double_vrule_on_left(int);
172 class simple_entry : public table_entry {
173 public:
174 simple_entry(const table *, const entry_modifier *);
175 void print();
176 void position_vertically();
177 simple_entry *to_simple_entry();
178 virtual void add_tab();
179 virtual void simple_print(int);
182 class empty_entry : public simple_entry {
183 public:
184 empty_entry(const table *, const entry_modifier *);
185 int line_type();
188 class text_entry : public simple_entry {
189 protected:
190 char *contents;
191 void print_contents();
192 public:
193 text_entry(const table *, const entry_modifier *, char *);
194 ~text_entry();
197 void text_entry::print_contents()
199 set_inline_modifier(mod);
200 prints(contents);
201 restore_inline_modifier(mod);
204 class repeated_char_entry : public text_entry {
205 public:
206 repeated_char_entry(const table *, const entry_modifier *, char *);
207 void simple_print(int);
210 class simple_text_entry : public text_entry {
211 public:
212 simple_text_entry(const table *, const entry_modifier *, char *);
213 void do_width();
216 class left_text_entry : public simple_text_entry {
217 public:
218 left_text_entry(const table *, const entry_modifier *, char *);
219 void simple_print(int);
220 void add_tab();
223 class right_text_entry : public simple_text_entry {
224 public:
225 right_text_entry(const table *, const entry_modifier *, char *);
226 void simple_print(int);
227 void add_tab();
230 class center_text_entry : public simple_text_entry {
231 public:
232 center_text_entry(const table *, const entry_modifier *, char *);
233 void simple_print(int);
234 void add_tab();
237 class numeric_text_entry : public text_entry {
238 int dot_pos;
239 public:
240 numeric_text_entry(const table *, const entry_modifier *, char *, int);
241 void do_width();
242 void simple_print(int);
245 class alphabetic_text_entry : public text_entry {
246 public:
247 alphabetic_text_entry(const table *, const entry_modifier *, char *);
248 void do_width();
249 void simple_print(int);
250 void add_tab();
253 class line_entry : public simple_entry {
254 protected:
255 char double_vrule_on_right;
256 char double_vrule_on_left;
257 public:
258 line_entry(const table *, const entry_modifier *);
259 void note_double_vrule_on_right(int);
260 void note_double_vrule_on_left(int);
261 void simple_print(int) = 0;
264 class single_line_entry : public line_entry {
265 public:
266 single_line_entry(const table *, const entry_modifier *);
267 void simple_print(int);
268 single_line_entry *to_single_line_entry();
269 int line_type();
272 class double_line_entry : public line_entry {
273 public:
274 double_line_entry(const table *, const entry_modifier *);
275 void simple_print(int);
276 double_line_entry *to_double_line_entry();
277 int line_type();
280 class short_line_entry : public simple_entry {
281 public:
282 short_line_entry(const table *, const entry_modifier *);
283 void simple_print(int);
284 int line_type();
287 class short_double_line_entry : public simple_entry {
288 public:
289 short_double_line_entry(const table *, const entry_modifier *);
290 void simple_print(int);
291 int line_type();
294 class block_entry : public table_entry {
295 char *contents;
296 protected:
297 void do_divert(int, int, const string *, int *, int);
298 public:
299 block_entry(const table *, const entry_modifier *, char *);
300 ~block_entry();
301 int divert(int, const string *, int *, int);
302 void do_depth();
303 void position_vertically();
304 void print() = 0;
307 class left_block_entry : public block_entry {
308 public:
309 left_block_entry(const table *, const entry_modifier *, char *);
310 void print();
313 class right_block_entry : public block_entry {
314 public:
315 right_block_entry(const table *, const entry_modifier *, char *);
316 void print();
319 class center_block_entry : public block_entry {
320 public:
321 center_block_entry(const table *, const entry_modifier *, char *);
322 void print();
325 class alphabetic_block_entry : public block_entry {
326 public:
327 alphabetic_block_entry(const table *, const entry_modifier *, char *);
328 void print();
329 int divert(int, const string *, int *, int);
332 table_entry::table_entry(const table *p, const entry_modifier *m)
333 : next(0), input_lineno(-1), input_filename(0),
334 start_row(-1), end_row(-1), start_col(-1), end_col(-1), parent(p), mod(m)
338 table_entry::~table_entry()
342 int table_entry::divert(int, const string *, int *, int)
344 return 0;
347 void table_entry::do_width()
351 single_line_entry *table_entry::to_single_line_entry()
353 return 0;
356 double_line_entry *table_entry::to_double_line_entry()
358 return 0;
361 simple_entry *table_entry::to_simple_entry()
363 return 0;
366 void table_entry::do_depth()
370 void table_entry::set_location()
372 set_troff_location(input_filename, input_lineno);
375 int table_entry::line_type()
377 return -1;
380 void table_entry::note_double_vrule_on_right(int)
384 void table_entry::note_double_vrule_on_left(int)
388 simple_entry::simple_entry(const table *p, const entry_modifier *m)
389 : table_entry(p, m)
393 void simple_entry::add_tab()
395 // do nothing
398 void simple_entry::simple_print(int)
400 // do nothing
403 void simple_entry::position_vertically()
405 if (start_row != end_row)
406 switch (mod->vertical_alignment) {
407 case entry_modifier::TOP:
408 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
409 break;
410 case entry_modifier::CENTER:
411 // Peform the motion in two stages so that the center is rounded
412 // vertically upwards even if net vertical motion is upwards.
413 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
414 printfs(".sp \\n[" BOTTOM_REG "]u-\\n[%1]u-1v/2u\n",
415 row_start_reg(start_row));
416 break;
417 case entry_modifier::BOTTOM:
418 printfs(".sp |\\n[%1]u+\\n[" BOTTOM_REG "]u-\\n[%1]u-1v\n",
419 row_start_reg(start_row));
420 break;
421 default:
422 assert(0);
426 void simple_entry::print()
428 prints(".ta");
429 add_tab();
430 prints('\n');
431 set_location();
432 prints("\\&");
433 simple_print(0);
434 prints('\n');
437 simple_entry *simple_entry::to_simple_entry()
439 return this;
442 empty_entry::empty_entry(const table *p, const entry_modifier *m)
443 : simple_entry(p, m)
447 int empty_entry::line_type()
449 return 0;
452 text_entry::text_entry(const table *p, const entry_modifier *m, char *s)
453 : simple_entry(p, m), contents(s)
457 text_entry::~text_entry()
459 a_delete contents;
462 repeated_char_entry::repeated_char_entry(const table *p,
463 const entry_modifier *m, char *s)
464 : text_entry(p, m, s)
468 void repeated_char_entry::simple_print(int)
470 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
471 set_inline_modifier(mod);
472 printfs("\\l" DELIMITER_CHAR "\\n[%1]u\\&",
473 span_width_reg(start_col, end_col));
474 prints(contents);
475 prints(DELIMITER_CHAR);
476 restore_inline_modifier(mod);
479 simple_text_entry::simple_text_entry(const table *p,
480 const entry_modifier *m, char *s)
481 : text_entry(p, m, s)
485 void simple_text_entry::do_width()
487 set_location();
488 printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR,
489 span_width_reg(start_col, end_col));
490 print_contents();
491 prints(DELIMITER_CHAR "\n");
494 left_text_entry::left_text_entry(const table *p,
495 const entry_modifier *m, char *s)
496 : simple_text_entry(p, m, s)
500 void left_text_entry::simple_print(int)
502 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
503 print_contents();
506 // The only point of this is to make `\a' ``work'' as in Unix tbl. Grrr.
508 void left_text_entry::add_tab()
510 printfs(" \\n[%1]u", column_end_reg(end_col));
513 right_text_entry::right_text_entry(const table *p,
514 const entry_modifier *m, char *s)
515 : simple_text_entry(p, m, s)
519 void right_text_entry::simple_print(int)
521 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
522 prints("\002\003");
523 print_contents();
524 prints("\002");
527 void right_text_entry::add_tab()
529 printfs(" \\n[%1]u", column_end_reg(end_col));
532 center_text_entry::center_text_entry(const table *p,
533 const entry_modifier *m, char *s)
534 : simple_text_entry(p, m, s)
538 void center_text_entry::simple_print(int)
540 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
541 prints("\002\003");
542 print_contents();
543 prints("\003\002");
546 void center_text_entry::add_tab()
548 printfs(" \\n[%1]u", column_end_reg(end_col));
551 numeric_text_entry::numeric_text_entry(const table *p,
552 const entry_modifier *m,
553 char *s, int pos)
554 : text_entry(p, m, s), dot_pos(pos)
558 void numeric_text_entry::do_width()
560 if (dot_pos != 0) {
561 set_location();
562 printfs(".nr %1 0\\w" DELIMITER_CHAR,
563 block_width_reg(start_row, start_col));
564 set_inline_modifier(mod);
565 for (int i = 0; i < dot_pos; i++)
566 prints(contents[i]);
567 restore_inline_modifier(mod);
568 prints(DELIMITER_CHAR "\n");
569 printfs(".nr %1 \\n[%1]>?\\n[%2]\n",
570 span_left_numeric_width_reg(start_col, end_col),
571 block_width_reg(start_row, start_col));
573 else
574 printfs(".nr %1 0\n", block_width_reg(start_row, start_col));
575 if (contents[dot_pos] != '\0') {
576 set_location();
577 printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR,
578 span_right_numeric_width_reg(start_col, end_col));
579 set_inline_modifier(mod);
580 prints(contents + dot_pos);
581 restore_inline_modifier(mod);
582 prints(DELIMITER_CHAR "\n");
586 void numeric_text_entry::simple_print(int)
588 printfs("\\h'|(\\n[%1]u-\\n[%2]u-\\n[%3]u/2u+\\n[%2]u+\\n[%4]u-\\n[%5]u)'",
589 span_width_reg(start_col, end_col),
590 span_left_numeric_width_reg(start_col, end_col),
591 span_right_numeric_width_reg(start_col, end_col),
592 column_start_reg(start_col),
593 block_width_reg(start_row, start_col));
594 print_contents();
597 alphabetic_text_entry::alphabetic_text_entry(const table *p,
598 const entry_modifier *m,
599 char *s)
600 : text_entry(p, m, s)
604 void alphabetic_text_entry::do_width()
606 set_location();
607 printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR,
608 span_alphabetic_width_reg(start_col, end_col));
609 print_contents();
610 prints(DELIMITER_CHAR "\n");
613 void alphabetic_text_entry::simple_print(int)
615 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
616 printfs("\\h'\\n[%1]u-\\n[%2]u/2u'",
617 span_width_reg(start_col, end_col),
618 span_alphabetic_width_reg(start_col, end_col));
619 print_contents();
622 // The only point of this is to make `\a' ``work'' as in Unix tbl. Grrr.
624 void alphabetic_text_entry::add_tab()
626 printfs(" \\n[%1]u", column_end_reg(end_col));
629 block_entry::block_entry(const table *p, const entry_modifier *m, char *s)
630 : table_entry(p, m), contents(s)
634 block_entry::~block_entry()
636 a_delete contents;
639 void block_entry::position_vertically()
641 if (start_row != end_row)
642 switch(mod->vertical_alignment) {
643 case entry_modifier::TOP:
644 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
645 break;
646 case entry_modifier::CENTER:
647 // Peform the motion in two stages so that the center is rounded
648 // vertically upwards even if net vertical motion is upwards.
649 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
650 printfs(".sp \\n[" BOTTOM_REG "]u-\\n[%1]u-\\n[%2]u/2u\n",
651 row_start_reg(start_row),
652 block_height_reg(start_row, start_col));
653 break;
654 case entry_modifier::BOTTOM:
655 printfs(".sp |\\n[%1]u+\\n[" BOTTOM_REG "]u-\\n[%1]u-\\n[%2]u\n",
656 row_start_reg(start_row),
657 block_height_reg(start_row, start_col));
658 break;
659 default:
660 assert(0);
662 if (mod->stagger)
663 prints(".sp -.5v\n");
666 int block_entry::divert(int ncols, const string *mw, int *sep, int do_expand)
668 do_divert(0, ncols, mw, sep, do_expand);
669 return 1;
672 void block_entry::do_divert(int alphabetic, int ncols, const string *mw,
673 int *sep, int do_expand)
675 int i;
676 for (i = start_col; i <= end_col; i++)
677 if (parent->expand[i])
678 break;
679 if (i > end_col) {
680 if (do_expand)
681 return;
683 else {
684 if (!do_expand)
685 return;
687 printfs(".di %1\n", block_diversion_name(start_row, start_col));
688 prints(".if \\n[" SAVED_FILL_REG "] .fi\n"
689 ".in 0\n");
690 prints(".ll ");
691 for (i = start_col; i <= end_col; i++)
692 if (mw[i].empty() && !parent->expand[i])
693 break;
694 if (i > end_col) {
695 // Every column spanned by this entry has a minimum width.
696 for (int j = start_col; j <= end_col; j++) {
697 if (j > start_col) {
698 if (sep)
699 printfs("+%1n", as_string(sep[j - 1]));
700 prints('+');
702 if (parent->expand[j])
703 prints("\\n[" EXPAND_REG "]u");
704 else
705 printfs("(n;%1)", mw[j]);
707 printfs(">?\\n[%1]u", span_width_reg(start_col, end_col));
709 else
710 // Assign each column with a block entry 1/(n+1) of the line
711 // width, where n is the column count.
712 printfs("(u;\\n[%1]>?(\\n[.l]*%2/%3))",
713 span_width_reg(start_col, end_col),
714 as_string(end_col - start_col + 1),
715 as_string(ncols + 1));
716 if (alphabetic)
717 prints("-2n");
718 prints("\n");
719 prints(".cp \\n(" COMPATIBLE_REG "\n");
720 set_modifier(mod);
721 set_location();
722 prints(contents);
723 prints(".br\n.di\n.cp 0\n");
724 if (!mod->zero_width) {
725 if (alphabetic) {
726 printfs(".nr %1 \\n[%1]>?(\\n[dl]+2n)\n",
727 span_width_reg(start_col, end_col));
728 printfs(".nr %1 \\n[%1]>?\\n[dl]\n",
729 span_alphabetic_width_reg(start_col, end_col));
731 else
732 printfs(".nr %1 \\n[%1]>?\\n[dl]\n",
733 span_width_reg(start_col, end_col));
735 printfs(".nr %1 \\n[dn]\n", block_height_reg(start_row, start_col));
736 printfs(".nr %1 \\n[dl]\n", block_width_reg(start_row, start_col));
737 prints("." RESET_MACRO_NAME "\n"
738 ".in \\n[" SAVED_INDENT_REG "]u\n"
739 ".nf\n");
740 // the block might have contained .lf commands
741 location_force_filename = 1;
744 void block_entry::do_depth()
746 printfs(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?(\\n[%1]+\\n[%2])\n",
747 row_start_reg(start_row),
748 block_height_reg(start_row, start_col));
751 left_block_entry::left_block_entry(const table *p,
752 const entry_modifier *m, char *s)
753 : block_entry(p, m, s)
757 void left_block_entry::print()
759 printfs(".in +\\n[%1]u\n", column_start_reg(start_col));
760 printfs(".%1\n", block_diversion_name(start_row, start_col));
761 prints(".in\n");
764 right_block_entry::right_block_entry(const table *p,
765 const entry_modifier *m, char *s)
766 : block_entry(p, m, s)
770 void right_block_entry::print()
772 printfs(".in +\\n[%1]u+\\n[%2]u-\\n[%3]u\n",
773 column_start_reg(start_col),
774 span_width_reg(start_col, end_col),
775 block_width_reg(start_row, start_col));
776 printfs(".%1\n", block_diversion_name(start_row, start_col));
777 prints(".in\n");
780 center_block_entry::center_block_entry(const table *p,
781 const entry_modifier *m, char *s)
782 : block_entry(p, m, s)
786 void center_block_entry::print()
788 printfs(".in +\\n[%1]u+(\\n[%2]u-\\n[%3]u/2u)\n",
789 column_start_reg(start_col),
790 span_width_reg(start_col, end_col),
791 block_width_reg(start_row, start_col));
792 printfs(".%1\n", block_diversion_name(start_row, start_col));
793 prints(".in\n");
796 alphabetic_block_entry::alphabetic_block_entry(const table *p,
797 const entry_modifier *m,
798 char *s)
799 : block_entry(p, m, s)
803 int alphabetic_block_entry::divert(int ncols, const string *mw, int *sep,
804 int do_expand)
806 do_divert(1, ncols, mw, sep, do_expand);
807 return 1;
810 void alphabetic_block_entry::print()
812 printfs(".in +\\n[%1]u+(\\n[%2]u-\\n[%3]u/2u)\n",
813 column_start_reg(start_col),
814 span_width_reg(start_col, end_col),
815 span_alphabetic_width_reg(start_col, end_col));
816 printfs(".%1\n", block_diversion_name(start_row, start_col));
817 prints(".in\n");
820 line_entry::line_entry(const table *p, const entry_modifier *m)
821 : simple_entry(p, m), double_vrule_on_right(0), double_vrule_on_left(0)
825 void line_entry::note_double_vrule_on_right(int is_corner)
827 double_vrule_on_right = is_corner ? 1 : 2;
830 void line_entry::note_double_vrule_on_left(int is_corner)
832 double_vrule_on_left = is_corner ? 1 : 2;
835 single_line_entry::single_line_entry(const table *p, const entry_modifier *m)
836 : line_entry(p, m)
840 int single_line_entry::line_type()
842 return 1;
845 void single_line_entry::simple_print(int dont_move)
847 printfs("\\h'|\\n[%1]u",
848 column_divide_reg(start_col));
849 if (double_vrule_on_left) {
850 prints(double_vrule_on_left == 1 ? "-" : "+");
851 prints(HALF_DOUBLE_LINE_SEP);
853 prints("'");
854 if (!dont_move)
855 prints("\\v'-" BAR_HEIGHT "'");
856 printfs("\\s[\\n[" LINESIZE_REG "]]" "\\D'l |\\n[%1]u",
857 column_divide_reg(end_col+1));
858 if (double_vrule_on_right) {
859 prints(double_vrule_on_left == 1 ? "+" : "-");
860 prints(HALF_DOUBLE_LINE_SEP);
862 prints("0'\\s0");
863 if (!dont_move)
864 prints("\\v'" BAR_HEIGHT "'");
867 single_line_entry *single_line_entry::to_single_line_entry()
869 return this;
872 double_line_entry::double_line_entry(const table *p, const entry_modifier *m)
873 : line_entry(p, m)
877 int double_line_entry::line_type()
879 return 2;
882 void double_line_entry::simple_print(int dont_move)
884 if (!dont_move)
885 prints("\\v'-" BAR_HEIGHT "'");
886 printfs("\\h'|\\n[%1]u",
887 column_divide_reg(start_col));
888 if (double_vrule_on_left) {
889 prints(double_vrule_on_left == 1 ? "-" : "+");
890 prints(HALF_DOUBLE_LINE_SEP);
892 prints("'");
893 printfs("\\v'-" HALF_DOUBLE_LINE_SEP "'"
894 "\\s[\\n[" LINESIZE_REG "]]"
895 "\\D'l |\\n[%1]u",
896 column_divide_reg(end_col+1));
897 if (double_vrule_on_right)
898 prints("-" HALF_DOUBLE_LINE_SEP);
899 prints(" 0'");
900 printfs("\\v'" DOUBLE_LINE_SEP "'"
901 "\\D'l |\\n[%1]u",
902 column_divide_reg(start_col));
903 if (double_vrule_on_right) {
904 prints(double_vrule_on_left == 1 ? "+" : "-");
905 prints(HALF_DOUBLE_LINE_SEP);
907 prints(" 0'");
908 prints("\\s0"
909 "\\v'-" HALF_DOUBLE_LINE_SEP "'");
910 if (!dont_move)
911 prints("\\v'" BAR_HEIGHT "'");
914 double_line_entry *double_line_entry::to_double_line_entry()
916 return this;
919 short_line_entry::short_line_entry(const table *p, const entry_modifier *m)
920 : simple_entry(p, m)
924 int short_line_entry::line_type()
926 return 1;
929 void short_line_entry::simple_print(int dont_move)
931 if (mod->stagger)
932 prints("\\v'-.5v'");
933 if (!dont_move)
934 prints("\\v'-" BAR_HEIGHT "'");
935 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
936 printfs("\\s[\\n[" LINESIZE_REG "]]"
937 "\\D'l \\n[%1]u 0'"
938 "\\s0",
939 span_width_reg(start_col, end_col));
940 if (!dont_move)
941 prints("\\v'" BAR_HEIGHT "'");
942 if (mod->stagger)
943 prints("\\v'.5v'");
946 short_double_line_entry::short_double_line_entry(const table *p,
947 const entry_modifier *m)
948 : simple_entry(p, m)
952 int short_double_line_entry::line_type()
954 return 2;
957 void short_double_line_entry::simple_print(int dont_move)
959 if (mod->stagger)
960 prints("\\v'-.5v'");
961 if (!dont_move)
962 prints("\\v'-" BAR_HEIGHT "'");
963 printfs("\\h'|\\n[%2]u'"
964 "\\v'-" HALF_DOUBLE_LINE_SEP "'"
965 "\\s[\\n[" LINESIZE_REG "]]"
966 "\\D'l \\n[%1]u 0'"
967 "\\v'" DOUBLE_LINE_SEP "'"
968 "\\D'l |\\n[%2]u 0'"
969 "\\s0"
970 "\\v'-" HALF_DOUBLE_LINE_SEP "'",
971 span_width_reg(start_col, end_col),
972 column_start_reg(start_col));
973 if (!dont_move)
974 prints("\\v'" BAR_HEIGHT "'");
975 if (mod->stagger)
976 prints("\\v'.5v'");
979 void set_modifier(const entry_modifier *m)
981 if (!m->font.empty())
982 printfs(".ft %1\n", m->font);
983 if (m->point_size.val != 0) {
984 prints(".ps ");
985 if (m->point_size.inc > 0)
986 prints('+');
987 else if (m->point_size.inc < 0)
988 prints('-');
989 printfs("%1\n", as_string(m->point_size.val));
991 if (m->vertical_spacing.val != 0) {
992 prints(".vs ");
993 if (m->vertical_spacing.inc > 0)
994 prints('+');
995 else if (m->vertical_spacing.inc < 0)
996 prints('-');
997 printfs("%1\n", as_string(m->vertical_spacing.val));
999 if (!m->macro.empty())
1000 printfs(".%1\n", m->macro);
1003 void set_inline_modifier(const entry_modifier *m)
1005 if (!m->font.empty())
1006 printfs("\\f[%1]", m->font);
1007 if (m->point_size.val != 0) {
1008 prints("\\s[");
1009 if (m->point_size.inc > 0)
1010 prints('+');
1011 else if (m->point_size.inc < 0)
1012 prints('-');
1013 printfs("%1]", as_string(m->point_size.val));
1015 if (m->stagger)
1016 prints("\\v'-.5v'");
1019 void restore_inline_modifier(const entry_modifier *m)
1021 if (!m->font.empty())
1022 prints("\\f[\\n[" SAVED_FONT_REG "]]");
1023 if (m->point_size.val != 0)
1024 prints("\\s[\\n[" SAVED_SIZE_REG "]]");
1025 if (m->stagger)
1026 prints("\\v'.5v'");
1029 struct stuff {
1030 stuff *next;
1031 int row; // occurs before row `row'
1032 char printed; // has it been printed?
1034 stuff(int);
1035 virtual void print(table *) = 0;
1036 virtual ~stuff();
1037 virtual int is_single_line() { return 0; };
1038 virtual int is_double_line() { return 0; };
1041 stuff::stuff(int r) : next(0), row(r), printed(0)
1045 stuff::~stuff()
1049 struct text_stuff : public stuff {
1050 string contents;
1051 const char *filename;
1052 int lineno;
1054 text_stuff(const string &, int, const char *, int);
1055 ~text_stuff();
1056 void print(table *);
1059 text_stuff::text_stuff(const string &s, int r, const char *fn, int ln)
1060 : stuff(r), contents(s), filename(fn), lineno(ln)
1064 text_stuff::~text_stuff()
1068 void text_stuff::print(table *)
1070 printed = 1;
1071 prints(".cp \\n(" COMPATIBLE_REG "\n");
1072 set_troff_location(filename, lineno);
1073 prints(contents);
1074 prints(".cp 0\n");
1075 location_force_filename = 1; // it might have been a .lf command
1078 struct single_hline_stuff : public stuff {
1079 single_hline_stuff(int);
1080 void print(table *);
1081 int is_single_line();
1084 single_hline_stuff::single_hline_stuff(int r) : stuff(r)
1088 void single_hline_stuff::print(table *tbl)
1090 printed = 1;
1091 tbl->print_single_hline(row);
1094 int single_hline_stuff::is_single_line()
1096 return 1;
1099 struct double_hline_stuff : stuff {
1100 double_hline_stuff(int);
1101 void print(table *);
1102 int is_double_line();
1105 double_hline_stuff::double_hline_stuff(int r) : stuff(r)
1109 void double_hline_stuff::print(table *tbl)
1111 printed = 1;
1112 tbl->print_double_hline(row);
1115 int double_hline_stuff::is_double_line()
1117 return 1;
1120 struct vertical_rule {
1121 vertical_rule *next;
1122 int start_row;
1123 int end_row;
1124 int col;
1125 char is_double;
1126 string top_adjust;
1127 string bot_adjust;
1129 vertical_rule(int, int, int, int, vertical_rule *);
1130 ~vertical_rule();
1131 void contribute_to_bottom_macro(table *);
1132 void print();
1135 vertical_rule::vertical_rule(int sr, int er, int c, int dbl,
1136 vertical_rule *p)
1137 : next(p), start_row(sr), end_row(er), col(c), is_double(dbl)
1141 vertical_rule::~vertical_rule()
1145 void vertical_rule::contribute_to_bottom_macro(table *tbl)
1147 printfs(".if \\n[" CURRENT_ROW_REG "]>=%1",
1148 as_string(start_row));
1149 if (end_row != tbl->get_nrows() - 1)
1150 printfs("&(\\n[" CURRENT_ROW_REG "]<%1)",
1151 as_string(end_row));
1152 prints(" \\{");
1153 printfs(".if %1<=\\n[" LAST_PASSED_ROW_REG "] .nr %2 \\n[#T]\n",
1154 as_string(start_row),
1155 row_top_reg(start_row));
1156 const char *offset_table[3];
1157 if (is_double) {
1158 offset_table[0] = "-" HALF_DOUBLE_LINE_SEP;
1159 offset_table[1] = "+" HALF_DOUBLE_LINE_SEP;
1160 offset_table[2] = 0;
1162 else {
1163 offset_table[0] = "";
1164 offset_table[1] = 0;
1166 for (const char **offsetp = offset_table; *offsetp; offsetp++) {
1167 prints(".sp -1\n"
1168 "\\v'" BODY_DEPTH);
1169 if (!bot_adjust.empty())
1170 printfs("+%1", bot_adjust);
1171 prints("'");
1172 printfs("\\h'\\n[%1]u%3'\\s[\\n[" LINESIZE_REG "]]\\D'l 0 |\\n[%2]u-1v",
1173 column_divide_reg(col),
1174 row_top_reg(start_row),
1175 *offsetp);
1176 if (!bot_adjust.empty())
1177 printfs("-(%1)", bot_adjust);
1178 // don't perform the top adjustment if the top is actually #T
1179 if (!top_adjust.empty())
1180 printfs("+((%1)*(%2>\\n[" LAST_PASSED_ROW_REG "]))",
1181 top_adjust,
1182 as_string(start_row));
1183 prints("'\\s0\n");
1185 prints(".\\}\n");
1188 void vertical_rule::print()
1190 printfs("\\*[" TRANSPARENT_STRING_NAME "]"
1191 ".if %1<=\\*[" QUOTE_STRING_NAME "]\\n[" LAST_PASSED_ROW_REG "] "
1192 ".nr %2 \\*[" QUOTE_STRING_NAME "]\\n[#T]\n",
1193 as_string(start_row),
1194 row_top_reg(start_row));
1195 const char *offset_table[3];
1196 if (is_double) {
1197 offset_table[0] = "-" HALF_DOUBLE_LINE_SEP;
1198 offset_table[1] = "+" HALF_DOUBLE_LINE_SEP;
1199 offset_table[2] = 0;
1201 else {
1202 offset_table[0] = "";
1203 offset_table[1] = 0;
1205 for (const char **offsetp = offset_table; *offsetp; offsetp++) {
1206 prints("\\*[" TRANSPARENT_STRING_NAME "].sp -1\n"
1207 "\\*[" TRANSPARENT_STRING_NAME "]\\v'" BODY_DEPTH);
1208 if (!bot_adjust.empty())
1209 printfs("+%1", bot_adjust);
1210 prints("'");
1211 printfs("\\h'\\n[%1]u%3'"
1212 "\\s[\\n[" LINESIZE_REG "]]"
1213 "\\D'l 0 |\\*[" QUOTE_STRING_NAME "]\\n[%2]u-1v",
1214 column_divide_reg(col),
1215 row_top_reg(start_row),
1216 *offsetp);
1217 if (!bot_adjust.empty())
1218 printfs("-(%1)", bot_adjust);
1219 // don't perform the top adjustment if the top is actually #T
1220 if (!top_adjust.empty())
1221 printfs("+((%1)*(%2>\\*[" QUOTE_STRING_NAME "]\\n["
1222 LAST_PASSED_ROW_REG "]))",
1223 top_adjust,
1224 as_string(start_row));
1225 prints("'"
1226 "\\s0\n");
1230 table::table(int nc, unsigned f, int ls, char dpc)
1231 : nrows(0), ncolumns(nc), linesize(ls), decimal_point_char(dpc),
1232 vrule_list(0), stuff_list(0), span_list(0),
1233 entry_list(0), entry_list_tailp(&entry_list), entry(0),
1234 vline(0), row_is_all_lines(0), left_separation(0), right_separation(0),
1235 total_separation(0), allocated_rows(0), flags(f)
1237 minimum_width = new string[ncolumns];
1238 column_separation = ncolumns > 1 ? new int[ncolumns - 1] : 0;
1239 equal = new char[ncolumns];
1240 expand = new char[ncolumns];
1241 int i;
1242 for (i = 0; i < ncolumns; i++) {
1243 equal[i] = 0;
1244 expand[i] = 0;
1246 for (i = 0; i < ncolumns - 1; i++)
1247 column_separation[i] = DEFAULT_COLUMN_SEPARATION;
1248 delim[0] = delim[1] = '\0';
1251 table::~table()
1253 for (int i = 0; i < nrows; i++) {
1254 a_delete entry[i];
1255 a_delete vline[i];
1257 a_delete entry;
1258 a_delete vline;
1259 while (entry_list) {
1260 table_entry *tem = entry_list;
1261 entry_list = entry_list->next;
1262 delete tem;
1264 ad_delete(ncolumns) minimum_width;
1265 a_delete column_separation;
1266 a_delete equal;
1267 a_delete expand;
1268 while (stuff_list) {
1269 stuff *tem = stuff_list;
1270 stuff_list = stuff_list->next;
1271 delete tem;
1273 while (vrule_list) {
1274 vertical_rule *tem = vrule_list;
1275 vrule_list = vrule_list->next;
1276 delete tem;
1278 a_delete row_is_all_lines;
1279 while (span_list) {
1280 horizontal_span *tem = span_list;
1281 span_list = span_list->next;
1282 delete tem;
1286 void table::set_delim(char c1, char c2)
1288 delim[0] = c1;
1289 delim[1] = c2;
1292 void table::set_minimum_width(int c, const string &w)
1294 assert(c >= 0 && c < ncolumns);
1295 minimum_width[c] = w;
1298 void table::set_column_separation(int c, int n)
1300 assert(c >= 0 && c < ncolumns - 1);
1301 column_separation[c] = n;
1304 void table::set_equal_column(int c)
1306 assert(c >= 0 && c < ncolumns);
1307 equal[c] = 1;
1310 void table::set_expand_column(int c)
1312 assert(c >= 0 && c < ncolumns);
1313 expand[c] = 1;
1316 void table::add_stuff(stuff *p)
1318 stuff **pp;
1319 for (pp = &stuff_list; *pp; pp = &(*pp)->next)
1321 *pp = p;
1324 void table::add_text_line(int r, const string &s, const char *filename,
1325 int lineno)
1327 add_stuff(new text_stuff(s, r, filename, lineno));
1330 void table::add_single_hline(int r)
1332 add_stuff(new single_hline_stuff(r));
1335 void table::add_double_hline(int r)
1337 add_stuff(new double_hline_stuff(r));
1340 void table::allocate(int r)
1342 if (r >= nrows) {
1343 typedef table_entry **PPtable_entry; // work around g++ 1.36.1 bug
1344 if (r >= allocated_rows) {
1345 if (allocated_rows == 0) {
1346 allocated_rows = 16;
1347 if (allocated_rows <= r)
1348 allocated_rows = r + 1;
1349 entry = new PPtable_entry[allocated_rows];
1350 vline = new char*[allocated_rows];
1352 else {
1353 table_entry ***old_entry = entry;
1354 int old_allocated_rows = allocated_rows;
1355 allocated_rows *= 2;
1356 if (allocated_rows <= r)
1357 allocated_rows = r + 1;
1358 entry = new PPtable_entry[allocated_rows];
1359 memcpy(entry, old_entry, sizeof(table_entry**)*old_allocated_rows);
1360 a_delete old_entry;
1361 char **old_vline = vline;
1362 vline = new char*[allocated_rows];
1363 memcpy(vline, old_vline, sizeof(char*)*old_allocated_rows);
1364 a_delete old_vline;
1367 assert(allocated_rows > r);
1368 while (nrows <= r) {
1369 entry[nrows] = new table_entry*[ncolumns];
1370 int i;
1371 for (i = 0; i < ncolumns; i++)
1372 entry[nrows][i] = 0;
1373 vline[nrows] = new char[ncolumns+1];
1374 for (i = 0; i < ncolumns+1; i++)
1375 vline[nrows][i] = 0;
1376 nrows++;
1381 void table::do_hspan(int r, int c)
1383 assert(r >= 0 && c >= 0 && r < nrows && c < ncolumns);
1384 if (c == 0) {
1385 error("first column cannot be horizontally spanned");
1386 return;
1388 table_entry *e = entry[r][c];
1389 if (e) {
1390 assert(e->start_row <= r && r <= e->end_row
1391 && e->start_col <= c && c <= e->end_col
1392 && e->end_row - e->start_row > 0
1393 && e->end_col - e->start_col > 0);
1394 return;
1396 e = entry[r][c-1];
1397 // e can be 0 if we had an empty entry or an error
1398 if (e == 0)
1399 return;
1400 if (e->start_row != r) {
1403 ^ s */
1404 error("impossible horizontal span at row %1, column %2", r + 1, c + 1);
1406 else {
1407 e->end_col = c;
1408 entry[r][c] = e;
1412 void table::do_vspan(int r, int c)
1414 assert(r >= 0 && c >= 0 && r < nrows && c < ncolumns);
1415 if (r == 0) {
1416 error("first row cannot be vertically spanned");
1417 return;
1419 table_entry *e = entry[r][c];
1420 if (e) {
1421 assert(e->start_row <= r && r <= e->end_row
1422 && e->start_col <= c && c <= e->end_col
1423 && e->end_row - e->start_row > 0
1424 && e->end_col - e->start_col > 0);
1425 return;
1427 e = entry[r-1][c];
1428 // e can be 0 if we had an empty entry or an error
1429 if (e == 0)
1430 return;
1431 if (e->start_col != c) {
1432 /* l s
1433 l ^ */
1434 error("impossible vertical span at row %1, column %2", r + 1, c + 1);
1436 else {
1437 for (int i = c; i <= e->end_col; i++) {
1438 assert(entry[r][i] == 0);
1439 entry[r][i] = e;
1441 e->end_row = r;
1445 int find_decimal_point(const char *s, char decimal_point_char,
1446 const char *delim)
1448 if (s == 0 || *s == '\0')
1449 return -1;
1450 const char *p;
1451 int in_delim = 0; // is p within eqn delimiters?
1452 // tbl recognises \& even within eqn delimiters; I don't
1453 for (p = s; *p; p++)
1454 if (in_delim) {
1455 if (*p == delim[1])
1456 in_delim = 0;
1458 else if (*p == delim[0])
1459 in_delim = 1;
1460 else if (p[0] == '\\' && p[1] == '&')
1461 return p - s;
1462 int possible_pos = -1;
1463 in_delim = 0;
1464 for (p = s; *p; p++)
1465 if (in_delim) {
1466 if (*p == delim[1])
1467 in_delim = 0;
1469 else if (*p == delim[0])
1470 in_delim = 1;
1471 else if (p[0] == decimal_point_char && csdigit(p[1]))
1472 possible_pos = p - s;
1473 if (possible_pos >= 0)
1474 return possible_pos;
1475 in_delim = 0;
1476 for (p = s; *p; p++)
1477 if (in_delim) {
1478 if (*p == delim[1])
1479 in_delim = 0;
1481 else if (*p == delim[0])
1482 in_delim = 1;
1483 else if (csdigit(*p))
1484 possible_pos = p + 1 - s;
1485 return possible_pos;
1488 void table::add_entry(int r, int c, const string &str, const entry_format *f,
1489 const char *fn, int ln)
1491 allocate(r);
1492 table_entry *e = 0;
1493 if (str == "\\_") {
1494 e = new short_line_entry(this, f);
1496 else if (str == "\\=") {
1497 e = new short_double_line_entry(this, f);
1499 else if (str == "_") {
1500 single_line_entry *lefte;
1501 if (c > 0 && entry[r][c-1] != 0 &&
1502 (lefte = entry[r][c-1]->to_single_line_entry()) != 0
1503 && lefte->start_row == r
1504 && lefte->mod->stagger == f->stagger) {
1505 lefte->end_col = c;
1506 entry[r][c] = lefte;
1508 else
1509 e = new single_line_entry(this, f);
1511 else if (str == "=") {
1512 double_line_entry *lefte;
1513 if (c > 0 && entry[r][c-1] != 0 &&
1514 (lefte = entry[r][c-1]->to_double_line_entry()) != 0
1515 && lefte->start_row == r
1516 && lefte->mod->stagger == f->stagger) {
1517 lefte->end_col = c;
1518 entry[r][c] = lefte;
1520 else
1521 e = new double_line_entry(this, f);
1523 else if (str == "\\^") {
1524 do_vspan(r, c);
1526 else if (str.length() > 2 && str[0] == '\\' && str[1] == 'R') {
1527 if (str.search('\n') >= 0)
1528 error_with_file_and_line(fn, ln, "bad repeated character");
1529 else {
1530 char *s = str.substring(2, str.length() - 2).extract();
1531 e = new repeated_char_entry(this, f, s);
1534 else {
1535 int is_block = str.search('\n') >= 0;
1536 char *s;
1537 switch (f->type) {
1538 case FORMAT_SPAN:
1539 assert(str.empty());
1540 do_hspan(r, c);
1541 break;
1542 case FORMAT_LEFT:
1543 if (!str.empty()) {
1544 s = str.extract();
1545 if (is_block)
1546 e = new left_block_entry(this, f, s);
1547 else
1548 e = new left_text_entry(this, f, s);
1550 else
1551 e = new empty_entry(this, f);
1552 break;
1553 case FORMAT_CENTER:
1554 if (!str.empty()) {
1555 s = str.extract();
1556 if (is_block)
1557 e = new center_block_entry(this, f, s);
1558 else
1559 e = new center_text_entry(this, f, s);
1561 else
1562 e = new empty_entry(this, f);
1563 break;
1564 case FORMAT_RIGHT:
1565 if (!str.empty()) {
1566 s = str.extract();
1567 if (is_block)
1568 e = new right_block_entry(this, f, s);
1569 else
1570 e = new right_text_entry(this, f, s);
1572 else
1573 e = new empty_entry(this, f);
1574 break;
1575 case FORMAT_NUMERIC:
1576 if (!str.empty()) {
1577 s = str.extract();
1578 if (is_block) {
1579 error_with_file_and_line(fn, ln, "can't have numeric text block");
1580 e = new left_block_entry(this, f, s);
1582 else {
1583 int pos = find_decimal_point(s, decimal_point_char, delim);
1584 if (pos < 0)
1585 e = new center_text_entry(this, f, s);
1586 else
1587 e = new numeric_text_entry(this, f, s, pos);
1590 else
1591 e = new empty_entry(this, f);
1592 break;
1593 case FORMAT_ALPHABETIC:
1594 if (!str.empty()) {
1595 s = str.extract();
1596 if (is_block)
1597 e = new alphabetic_block_entry(this, f, s);
1598 else
1599 e = new alphabetic_text_entry(this, f, s);
1601 else
1602 e = new empty_entry(this, f);
1603 break;
1604 case FORMAT_VSPAN:
1605 do_vspan(r, c);
1606 break;
1607 case FORMAT_HLINE:
1608 if (str.length() != 0)
1609 error_with_file_and_line(fn, ln,
1610 "non-empty data entry for `_' format ignored");
1611 e = new single_line_entry(this, f);
1612 break;
1613 case FORMAT_DOUBLE_HLINE:
1614 if (str.length() != 0)
1615 error_with_file_and_line(fn, ln,
1616 "non-empty data entry for `=' format ignored");
1617 e = new double_line_entry(this, f);
1618 break;
1619 default:
1620 assert(0);
1623 if (e) {
1624 table_entry *preve = entry[r][c];
1625 if (preve) {
1626 /* c s
1627 ^ l */
1628 error_with_file_and_line(fn, ln, "row %1, column %2 already spanned",
1629 r + 1, c + 1);
1630 delete e;
1632 else {
1633 e->input_lineno = ln;
1634 e->input_filename = fn;
1635 e->start_row = e->end_row = r;
1636 e->start_col = e->end_col = c;
1637 *entry_list_tailp = e;
1638 entry_list_tailp = &e->next;
1639 entry[r][c] = e;
1644 // add vertical lines for row r
1646 void table::add_vlines(int r, const char *v)
1648 allocate(r);
1649 for (int i = 0; i < ncolumns+1; i++)
1650 vline[r][i] = v[i];
1653 void table::check()
1655 table_entry *p = entry_list;
1656 int i, j;
1657 while (p) {
1658 for (i = p->start_row; i <= p->end_row; i++)
1659 for (j = p->start_col; j <= p->end_col; j++)
1660 assert(entry[i][j] == p);
1661 p = p->next;
1665 void table::print()
1667 location_force_filename = 1;
1668 check();
1669 init_output();
1670 determine_row_type();
1671 compute_widths();
1672 if (!(flags & CENTER))
1673 prints(".if \\n[" SAVED_CENTER_REG "] \\{");
1674 prints(".in +(u;\\n[.l]-\\n[.i]-\\n[TW]/2>?-\\n[.i])\n"
1675 ".nr " SAVED_INDENT_REG " \\n[.i]\n");
1676 if (!(flags & CENTER))
1677 prints(".\\}\n");
1678 build_vrule_list();
1679 define_bottom_macro();
1680 do_top();
1681 for (int i = 0; i < nrows; i++)
1682 do_row(i);
1683 do_bottom();
1686 void table::determine_row_type()
1688 row_is_all_lines = new char[nrows];
1689 for (int i = 0; i < nrows; i++) {
1690 int had_single = 0;
1691 int had_double = 0;
1692 int had_non_line = 0;
1693 for (int c = 0; c < ncolumns; c++) {
1694 table_entry *e = entry[i][c];
1695 if (e != 0) {
1696 if (e->start_row == e->end_row) {
1697 int t = e->line_type();
1698 switch (t) {
1699 case -1:
1700 had_non_line = 1;
1701 break;
1702 case 0:
1703 // empty
1704 break;
1705 case 1:
1706 had_single = 1;
1707 break;
1708 case 2:
1709 had_double = 1;
1710 break;
1711 default:
1712 assert(0);
1714 if (had_non_line)
1715 break;
1717 c = e->end_col;
1720 if (had_non_line)
1721 row_is_all_lines[i] = 0;
1722 else if (had_double)
1723 row_is_all_lines[i] = 2;
1724 else if (had_single)
1725 row_is_all_lines[i] = 1;
1726 else
1727 row_is_all_lines[i] = 0;
1731 int table::count_expand_columns()
1733 int count = 0;
1734 for (int i = 0; i < ncolumns; i++)
1735 if (expand[i])
1736 count++;
1737 return count;
1740 void table::init_output()
1742 prints(".nr " COMPATIBLE_REG " \\n(.C\n"
1743 ".cp 0\n");
1744 if (linesize > 0)
1745 printfs(".nr " LINESIZE_REG " %1\n", as_string(linesize));
1746 else
1747 prints(".nr " LINESIZE_REG " \\n[.s]\n");
1748 if (!(flags & CENTER))
1749 prints(".nr " SAVED_CENTER_REG " \\n[.ce]\n");
1750 if (compatible_flag)
1751 prints(".ds " LEADER_REG " \\a\n");
1752 prints(".de " RESET_MACRO_NAME "\n"
1753 ".ft \\n[.f]\n"
1754 ".ps \\n[.s]\n"
1755 ".vs \\n[.v]u\n"
1756 ".in \\n[.i]u\n"
1757 ".ll \\n[.l]u\n"
1758 ".ls \\n[.L]\n"
1759 ".ad \\n[.j]\n"
1760 ".ie \\n[.u] .fi\n"
1761 ".el .nf\n"
1762 ".ce \\n[.ce]\n"
1763 "..\n"
1764 ".nr " SAVED_INDENT_REG " \\n[.i]\n"
1765 ".nr " SAVED_FONT_REG " \\n[.f]\n"
1766 ".nr " SAVED_SIZE_REG " \\n[.s]\n"
1767 ".nr " SAVED_FILL_REG " \\n[.u]\n"
1768 ".nr T. 0\n"
1769 ".nr " CURRENT_ROW_REG " 0-1\n"
1770 ".nr " LAST_PASSED_ROW_REG " 0-1\n"
1771 ".nr " SECTION_DIVERSION_FLAG_REG " 0\n"
1772 ".ds " TRANSPARENT_STRING_NAME "\n"
1773 ".ds " QUOTE_STRING_NAME "\n"
1774 ".nr " NEED_BOTTOM_RULE_REG " 1\n"
1775 ".nr " SUPPRESS_BOTTOM_REG " 0\n"
1776 ".eo\n"
1777 ".de " REPEATED_MARK_MACRO "\n"
1778 ".mk \\$1\n"
1779 ".if !'\\n(.z'' \\!." REPEATED_MARK_MACRO " \"\\$1\"\n"
1780 "..\n"
1781 ".de " REPEATED_VPT_MACRO "\n"
1782 ".vpt \\$1\n"
1783 ".if !'\\n(.z'' \\!." REPEATED_VPT_MACRO " \"\\$1\"\n"
1784 "..\n");
1785 if (!(flags & NOKEEP))
1786 prints(".de " KEEP_MACRO_NAME "\n"
1787 ".if '\\n[.z]'' \\{.ds " QUOTE_STRING_NAME " \\\\\n"
1788 ".ds " TRANSPARENT_STRING_NAME " \\!\n"
1789 ".di " SECTION_DIVERSION_NAME "\n"
1790 ".nr " SECTION_DIVERSION_FLAG_REG " 1\n"
1791 ".in 0\n"
1792 ".\\}\n"
1793 "..\n"
1794 ".de " RELEASE_MACRO_NAME "\n"
1795 ".if \\n[" SECTION_DIVERSION_FLAG_REG "] \\{"
1796 ".di\n"
1797 ".in \\n[" SAVED_INDENT_REG "]u\n"
1798 ".nr " SAVED_DN_REG " \\n[dn]\n"
1799 ".ds " QUOTE_STRING_NAME "\n"
1800 ".ds " TRANSPARENT_STRING_NAME "\n"
1801 ".nr " SECTION_DIVERSION_FLAG_REG " 0\n"
1802 ".if \\n[.t]<=\\n[dn] \\{"
1803 ".nr T. 1\n"
1804 ".T#\n"
1805 ".nr " SUPPRESS_BOTTOM_REG " 1\n"
1806 ".sp \\n[.t]u\n"
1807 ".nr " SUPPRESS_BOTTOM_REG " 0\n"
1808 ".mk #T\n"
1809 ".\\}\n"
1810 ".if \\n[.t]<=\\n[" SAVED_DN_REG "] "
1811 /* Since we turn off traps, it won't get into an infinite loop
1812 when we try and print it; it will just go off the bottom of the
1813 page. */
1814 ".tm warning: page \\n%: table text block will not fit on one page\n"
1815 ".nf\n"
1816 ".ls 1\n"
1817 "." SECTION_DIVERSION_NAME "\n"
1818 ".ls\n"
1819 ".rm " SECTION_DIVERSION_NAME "\n"
1820 ".\\}\n"
1821 "..\n"
1822 ".nr " TABLE_DIVERSION_FLAG_REG " 0\n"
1823 ".de " TABLE_KEEP_MACRO_NAME "\n"
1824 ".if '\\n[.z]'' \\{"
1825 ".di " TABLE_DIVERSION_NAME "\n"
1826 ".nr " TABLE_DIVERSION_FLAG_REG " 1\n"
1827 ".\\}\n"
1828 "..\n"
1829 ".de " TABLE_RELEASE_MACRO_NAME "\n"
1830 ".if \\n[" TABLE_DIVERSION_FLAG_REG "] \\{.br\n"
1831 ".di\n"
1832 ".nr " SAVED_DN_REG " \\n[dn]\n"
1833 ".ne \\n[dn]u+\\n[.V]u\n"
1834 ".ie \\n[.t]<=\\n[" SAVED_DN_REG "] "
1835 ".tm error: page \\n%: table will not fit on one page; use .TS H/.TH with a supporting macro package\n"
1836 ".el \\{"
1837 ".in 0\n"
1838 ".ls 1\n"
1839 ".nf\n"
1840 "." TABLE_DIVERSION_NAME "\n"
1841 ".\\}\n"
1842 ".rm " TABLE_DIVERSION_NAME "\n"
1843 ".\\}\n"
1844 "..\n");
1845 prints(".ec\n"
1846 ".ce 0\n"
1847 ".nf\n");
1850 string block_width_reg(int r, int c)
1852 static char name[sizeof(BLOCK_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1853 sprintf(name, BLOCK_WIDTH_PREFIX "%d,%d", r, c);
1854 return string(name);
1857 string block_diversion_name(int r, int c)
1859 static char name[sizeof(BLOCK_DIVERSION_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1860 sprintf(name, BLOCK_DIVERSION_PREFIX "%d,%d", r, c);
1861 return string(name);
1864 string block_height_reg(int r, int c)
1866 static char name[sizeof(BLOCK_HEIGHT_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1867 sprintf(name, BLOCK_HEIGHT_PREFIX "%d,%d", r, c);
1868 return string(name);
1871 string span_width_reg(int start_col, int end_col)
1873 static char name[sizeof(SPAN_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1874 sprintf(name, SPAN_WIDTH_PREFIX "%d", start_col);
1875 if (end_col != start_col)
1876 sprintf(strchr(name, '\0'), ",%d", end_col);
1877 return string(name);
1880 string span_left_numeric_width_reg(int start_col, int end_col)
1882 static char name[sizeof(SPAN_LEFT_NUMERIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1883 sprintf(name, SPAN_LEFT_NUMERIC_WIDTH_PREFIX "%d", start_col);
1884 if (end_col != start_col)
1885 sprintf(strchr(name, '\0'), ",%d", end_col);
1886 return string(name);
1889 string span_right_numeric_width_reg(int start_col, int end_col)
1891 static char name[sizeof(SPAN_RIGHT_NUMERIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1892 sprintf(name, SPAN_RIGHT_NUMERIC_WIDTH_PREFIX "%d", start_col);
1893 if (end_col != start_col)
1894 sprintf(strchr(name, '\0'), ",%d", end_col);
1895 return string(name);
1898 string span_alphabetic_width_reg(int start_col, int end_col)
1900 static char name[sizeof(SPAN_ALPHABETIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1901 sprintf(name, SPAN_ALPHABETIC_WIDTH_PREFIX "%d", start_col);
1902 if (end_col != start_col)
1903 sprintf(strchr(name, '\0'), ",%d", end_col);
1904 return string(name);
1907 string column_separation_reg(int col)
1909 static char name[sizeof(COLUMN_SEPARATION_PREFIX)+INT_DIGITS];
1910 sprintf(name, COLUMN_SEPARATION_PREFIX "%d", col);
1911 return string(name);
1914 string row_start_reg(int row)
1916 static char name[sizeof(ROW_START_PREFIX)+INT_DIGITS];
1917 sprintf(name, ROW_START_PREFIX "%d", row);
1918 return string(name);
1921 string column_start_reg(int col)
1923 static char name[sizeof(COLUMN_START_PREFIX)+INT_DIGITS];
1924 sprintf(name, COLUMN_START_PREFIX "%d", col);
1925 return string(name);
1928 string column_end_reg(int col)
1930 static char name[sizeof(COLUMN_END_PREFIX)+INT_DIGITS];
1931 sprintf(name, COLUMN_END_PREFIX "%d", col);
1932 return string(name);
1935 string column_divide_reg(int col)
1937 static char name[sizeof(COLUMN_DIVIDE_PREFIX)+INT_DIGITS];
1938 sprintf(name, COLUMN_DIVIDE_PREFIX "%d", col);
1939 return string(name);
1942 string row_top_reg(int row)
1944 static char name[sizeof(ROW_TOP_PREFIX)+INT_DIGITS];
1945 sprintf(name, ROW_TOP_PREFIX "%d", row);
1946 return string(name);
1949 void init_span_reg(int start_col, int end_col)
1951 printfs(".nr %1 \\n(.H\n.nr %2 0\n.nr %3 0\n.nr %4 0\n",
1952 span_width_reg(start_col, end_col),
1953 span_alphabetic_width_reg(start_col, end_col),
1954 span_left_numeric_width_reg(start_col, end_col),
1955 span_right_numeric_width_reg(start_col, end_col));
1958 void compute_span_width(int start_col, int end_col)
1960 printfs(".nr %1 \\n[%1]>?(\\n[%2]+\\n[%3])\n"
1961 ".if \\n[%4] .nr %1 \\n[%1]>?(\\n[%4]+2n)\n",
1962 span_width_reg(start_col, end_col),
1963 span_left_numeric_width_reg(start_col, end_col),
1964 span_right_numeric_width_reg(start_col, end_col),
1965 span_alphabetic_width_reg(start_col, end_col));
1968 // Increase the widths of columns so that the width of any spanning entry
1969 // is not greater than the sum of the widths of the columns that it spans.
1970 // Ensure that the widths of columns remain equal.
1972 void table::divide_span(int start_col, int end_col)
1974 assert(end_col > start_col);
1975 printfs(".nr " NEEDED_REG " \\n[%1]-(\\n[%2]",
1976 span_width_reg(start_col, end_col),
1977 span_width_reg(start_col, start_col));
1978 int i;
1979 for (i = start_col + 1; i <= end_col; i++) {
1980 // The column separation may shrink with the expand option.
1981 if (!(flags & EXPAND))
1982 printfs("+%1n", as_string(column_separation[i - 1]));
1983 printfs("+\\n[%1]", span_width_reg(i, i));
1985 prints(")\n");
1986 printfs(".nr " NEEDED_REG " \\n[" NEEDED_REG "]/%1\n",
1987 as_string(end_col - start_col + 1));
1988 prints(".if \\n[" NEEDED_REG "] \\{");
1989 for (i = start_col; i <= end_col; i++)
1990 printfs(".nr %1 +\\n[" NEEDED_REG "]\n",
1991 span_width_reg(i, i));
1992 int equal_flag = 0;
1993 for (i = start_col; i <= end_col && !equal_flag; i++)
1994 if (equal[i] || expand[i])
1995 equal_flag = 1;
1996 if (equal_flag) {
1997 for (i = 0; i < ncolumns; i++)
1998 if (i < start_col || i > end_col)
1999 printfs(".nr %1 +\\n[" NEEDED_REG "]\n",
2000 span_width_reg(i, i));
2002 prints(".\\}\n");
2005 void table::sum_columns(int start_col, int end_col, int do_expand)
2007 assert(end_col > start_col);
2008 int i;
2009 for (i = start_col; i <= end_col; i++)
2010 if (expand[i])
2011 break;
2012 if (i > end_col) {
2013 if (do_expand)
2014 return;
2016 else {
2017 if (!do_expand)
2018 return;
2020 printfs(".nr %1 \\n[%2]",
2021 span_width_reg(start_col, end_col),
2022 span_width_reg(start_col, start_col));
2023 for (i = start_col + 1; i <= end_col; i++)
2024 printfs("+(%1*\\n[" SEPARATION_FACTOR_REG "])+\\n[%2]",
2025 as_string(column_separation[i - 1]),
2026 span_width_reg(i, i));
2027 prints('\n');
2030 horizontal_span::horizontal_span(int sc, int ec, horizontal_span *p)
2031 : next(p), start_col(sc), end_col(ec)
2035 void table::build_span_list()
2037 span_list = 0;
2038 table_entry *p = entry_list;
2039 while (p) {
2040 if (p->end_col != p->start_col) {
2041 horizontal_span *q;
2042 for (q = span_list; q; q = q->next)
2043 if (q->start_col == p->start_col
2044 && q->end_col == p->end_col)
2045 break;
2046 if (!q)
2047 span_list = new horizontal_span(p->start_col, p->end_col, span_list);
2049 p = p->next;
2051 // Now sort span_list primarily by order of end_row, and secondarily
2052 // by reverse order of start_row. This ensures that if we divide
2053 // spans using the order in span_list, we will get reasonable results.
2054 horizontal_span *unsorted = span_list;
2055 span_list = 0;
2056 while (unsorted) {
2057 horizontal_span **pp;
2058 for (pp = &span_list; *pp; pp = &(*pp)->next)
2059 if (unsorted->end_col < (*pp)->end_col
2060 || (unsorted->end_col == (*pp)->end_col
2061 && (unsorted->start_col > (*pp)->start_col)))
2062 break;
2063 horizontal_span *tem = unsorted->next;
2064 unsorted->next = *pp;
2065 *pp = unsorted;
2066 unsorted = tem;
2070 void table::compute_expand_width()
2072 int i;
2073 int colcount = count_expand_columns();
2074 prints(".nr " EXPAND_REG " \\n[.l]-\\n[.i]");
2075 for (i = 0; i < ncolumns; i++)
2076 if (!expand[i])
2077 printfs("-\\n[%1]", span_width_reg(i, i));
2078 if (total_separation)
2079 printfs("-%1n", as_string(total_separation));
2080 prints("\n");
2081 prints(".if \\n[" EXPAND_REG "]<0 \\{");
2082 entry_list->set_location();
2083 prints(".tm warning: around line \\n[.c]: table wider than line width\n"
2084 ".nr " EXPAND_REG " 0\n"
2085 ".\\}\n");
2086 if (colcount > 1)
2087 printfs(".nr " EXPAND_REG " \\n[" EXPAND_REG "]/%1\n",
2088 as_string(colcount));
2089 for (i = 0; i < ncolumns; i++)
2090 if (expand[i])
2091 printfs(".nr %1 \\n[%1]>?\\n[" EXPAND_REG "]\n", span_width_reg(i, i));
2094 void table::compute_total_separation()
2096 if (flags & (ALLBOX | BOX | DOUBLEBOX))
2097 left_separation = right_separation = 1;
2098 else {
2099 for (int i = 0; i < nrows; i++) {
2100 if (vline[i][0] > 0)
2101 left_separation = 1;
2102 if (vline[i][ncolumns] > 0)
2103 right_separation = 1;
2106 total_separation = left_separation + right_separation;
2107 int i;
2108 for (i = 0; i < ncolumns - 1; i++)
2109 total_separation += column_separation[i];
2112 void table::compute_separation_factor()
2114 // Don't let the separation factor be negative.
2115 prints(".nr " SEPARATION_FACTOR_REG " \\n[.l]-\\n[.i]");
2116 for (int i = 0; i < ncolumns; i++)
2117 printfs("-\\n[%1]", span_width_reg(i, i));
2118 printfs("/%1\n", as_string(total_separation));
2119 prints(".ie \\n[" SEPARATION_FACTOR_REG "]<=0 \\{");
2120 entry_list->set_location();
2121 prints(".tm warning: around line \\n[.c]: column separation set to zero\n"
2122 ".nr " SEPARATION_FACTOR_REG " 0\n"
2123 ".\\}\n"
2124 ".el .if \\n[" SEPARATION_FACTOR_REG "]<1n \\{");
2125 entry_list->set_location();
2126 prints(".tm warning: around line \\n[.c]: table squeezed horizontally to fit line length\n"
2127 ".\\}\n");
2130 void table::compute_column_positions()
2132 printfs(".nr %1 0\n", column_divide_reg(0));
2133 printfs(".nr %1 %2*\\n[" SEPARATION_FACTOR_REG "]\n",
2134 column_start_reg(0),
2135 as_string(left_separation));
2136 int i;
2137 for (i = 1;; i++) {
2138 printfs(".nr %1 \\n[%2]+\\n[%3]\n",
2139 column_end_reg(i-1),
2140 column_start_reg(i-1),
2141 span_width_reg(i-1, i-1));
2142 if (i >= ncolumns)
2143 break;
2144 printfs(".nr %1 \\n[%2]+(%3*\\n[" SEPARATION_FACTOR_REG "])\n",
2145 column_start_reg(i),
2146 column_end_reg(i-1),
2147 as_string(column_separation[i-1]));
2148 printfs(".nr %1 \\n[%2]+\\n[%3]/2\n",
2149 column_divide_reg(i),
2150 column_end_reg(i-1),
2151 column_start_reg(i));
2153 printfs(".nr %1 \\n[%2]+(%3*\\n[" SEPARATION_FACTOR_REG "])\n",
2154 column_divide_reg(ncolumns),
2155 column_end_reg(i-1),
2156 as_string(right_separation));
2157 printfs(".nr TW \\n[%1]\n",
2158 column_divide_reg(ncolumns));
2159 if (flags & DOUBLEBOX) {
2160 printfs(".nr %1 +" DOUBLE_LINE_SEP "\n", column_divide_reg(0));
2161 printfs(".nr %1 -" DOUBLE_LINE_SEP "\n", column_divide_reg(ncolumns));
2165 void table::make_columns_equal()
2167 int first = -1; // index of first equal column
2168 int i;
2169 for (i = 0; i < ncolumns; i++)
2170 if (equal[i]) {
2171 if (first < 0) {
2172 printfs(".nr %1 \\n[%1]", span_width_reg(i, i));
2173 first = i;
2175 else
2176 printfs(">?\\n[%1]", span_width_reg(i, i));
2178 if (first >= 0) {
2179 prints('\n');
2180 for (i = first + 1; i < ncolumns; i++)
2181 if (equal[i])
2182 printfs(".nr %1 \\n[%2]\n",
2183 span_width_reg(i, i),
2184 span_width_reg(first, first));
2188 void table::compute_widths()
2190 build_span_list();
2191 int i;
2192 horizontal_span *p;
2193 // These values get refined later.
2194 prints(".nr " SEPARATION_FACTOR_REG " 1n\n");
2195 for (i = 0; i < ncolumns; i++) {
2196 init_span_reg(i, i);
2197 if (!minimum_width[i].empty())
2198 printfs(".nr %1 (n;%2)\n", span_width_reg(i, i), minimum_width[i]);
2200 for (p = span_list; p; p = p->next)
2201 init_span_reg(p->start_col, p->end_col);
2202 // Compute all field widths except for blocks.
2203 table_entry *q;
2204 for (q = entry_list; q; q = q->next)
2205 if (!q->mod->zero_width)
2206 q->do_width();
2207 // Compute all span widths, not handling blocks yet.
2208 for (i = 0; i < ncolumns; i++)
2209 compute_span_width(i, i);
2210 for (p = span_list; p; p = p->next)
2211 compute_span_width(p->start_col, p->end_col);
2212 // Making columns equal normally increases the width of some columns.
2213 make_columns_equal();
2214 // Note that divide_span keeps equal width columns equal.
2215 // This function might increase the width of some columns, too.
2216 for (p = span_list; p; p = p->next)
2217 divide_span(p->start_col, p->end_col);
2218 compute_total_separation();
2219 for (p = span_list; p; p = p->next)
2220 sum_columns(p->start_col, p->end_col, 0);
2221 // Now handle unexpanded blocks.
2222 int had_spanning_block = 0;
2223 int had_equal_block = 0;
2224 for (q = entry_list; q; q = q->next)
2225 if (q->divert(ncolumns, minimum_width,
2226 (flags & EXPAND) ? column_separation : 0, 0)) {
2227 if (q->end_col > q->start_col)
2228 had_spanning_block = 1;
2229 for (i = q->start_col; i <= q->end_col && !had_equal_block; i++)
2230 if (equal[i])
2231 had_equal_block = 1;
2233 // Adjust widths.
2234 if (had_equal_block)
2235 make_columns_equal();
2236 if (had_spanning_block)
2237 for (p = span_list; p; p = p->next)
2238 divide_span(p->start_col, p->end_col);
2239 compute_expand_width();
2240 if ((flags & EXPAND) && total_separation != 0) {
2241 compute_separation_factor();
2242 for (p = span_list; p; p = p->next)
2243 sum_columns(p->start_col, p->end_col, 0);
2245 else {
2246 // Handle expanded blocks.
2247 for (p = span_list; p; p = p->next)
2248 sum_columns(p->start_col, p->end_col, 1);
2249 for (q = entry_list; q; q = q->next)
2250 if (q->divert(ncolumns, minimum_width, 0, 1)) {
2251 if (q->end_col > q->start_col)
2252 had_spanning_block = 1;
2254 // Adjust widths again.
2255 if (had_spanning_block)
2256 for (p = span_list; p; p = p->next)
2257 divide_span(p->start_col, p->end_col);
2259 compute_column_positions();
2262 void table::print_single_hline(int r)
2264 prints(".vs " LINE_SEP ">?\\n[.V]u\n"
2265 ".ls 1\n"
2266 "\\v'" BODY_DEPTH "'"
2267 "\\s[\\n[" LINESIZE_REG "]]");
2268 if (r > nrows - 1)
2269 prints("\\D'l |\\n[TW]u 0'");
2270 else {
2271 int start_col = 0;
2272 for (;;) {
2273 while (start_col < ncolumns
2274 && entry[r][start_col] != 0
2275 && entry[r][start_col]->start_row != r)
2276 start_col++;
2277 int end_col;
2278 for (end_col = start_col;
2279 end_col < ncolumns
2280 && (entry[r][end_col] == 0
2281 || entry[r][end_col]->start_row == r);
2282 end_col++)
2284 if (end_col <= start_col)
2285 break;
2286 printfs("\\h'|\\n[%1]u",
2287 column_divide_reg(start_col));
2288 if ((r > 0 && vline[r-1][start_col] == 2)
2289 || (r < nrows && vline[r][start_col] == 2))
2290 prints("-" HALF_DOUBLE_LINE_SEP);
2291 prints("'");
2292 printfs("\\D'l |\\n[%1]u",
2293 column_divide_reg(end_col));
2294 if ((r > 0 && vline[r-1][end_col] == 2)
2295 || (r < nrows && vline[r][end_col] == 2))
2296 prints("+" HALF_DOUBLE_LINE_SEP);
2297 prints(" 0'");
2298 start_col = end_col;
2301 prints("\\s0\n");
2302 prints(".ls\n"
2303 ".vs\n");
2306 void table::print_double_hline(int r)
2308 prints(".vs " LINE_SEP "+" DOUBLE_LINE_SEP
2309 ">?\\n[.V]u\n"
2310 ".ls 1\n"
2311 "\\v'" BODY_DEPTH "'"
2312 "\\s[\\n[" LINESIZE_REG "]]");
2313 if (r > nrows - 1)
2314 prints("\\v'-" DOUBLE_LINE_SEP "'"
2315 "\\D'l |\\n[TW]u 0'"
2316 "\\v'" DOUBLE_LINE_SEP "'"
2317 "\\h'|0'"
2318 "\\D'l |\\n[TW]u 0'");
2319 else {
2320 int start_col = 0;
2321 for (;;) {
2322 while (start_col < ncolumns
2323 && entry[r][start_col] != 0
2324 && entry[r][start_col]->start_row != r)
2325 start_col++;
2326 int end_col;
2327 for (end_col = start_col;
2328 end_col < ncolumns
2329 && (entry[r][end_col] == 0
2330 || entry[r][end_col]->start_row == r);
2331 end_col++)
2333 if (end_col <= start_col)
2334 break;
2335 const char *left_adjust = 0;
2336 if ((r > 0 && vline[r-1][start_col] == 2)
2337 || (r < nrows && vline[r][start_col] == 2))
2338 left_adjust = "-" HALF_DOUBLE_LINE_SEP;
2339 const char *right_adjust = 0;
2340 if ((r > 0 && vline[r-1][end_col] == 2)
2341 || (r < nrows && vline[r][end_col] == 2))
2342 right_adjust = "+" HALF_DOUBLE_LINE_SEP;
2343 printfs("\\v'-" DOUBLE_LINE_SEP "'"
2344 "\\h'|\\n[%1]u",
2345 column_divide_reg(start_col));
2346 if (left_adjust)
2347 prints(left_adjust);
2348 prints("'");
2349 printfs("\\D'l |\\n[%1]u",
2350 column_divide_reg(end_col));
2351 if (right_adjust)
2352 prints(right_adjust);
2353 prints(" 0'");
2354 printfs("\\v'" DOUBLE_LINE_SEP "'"
2355 "\\h'|\\n[%1]u",
2356 column_divide_reg(start_col));
2357 if (left_adjust)
2358 prints(left_adjust);
2359 prints("'");
2360 printfs("\\D'l |\\n[%1]u",
2361 column_divide_reg(end_col));
2362 if (right_adjust)
2363 prints(right_adjust);
2364 prints(" 0'");
2365 start_col = end_col;
2368 prints("\\s0\n"
2369 ".ls\n"
2370 ".vs\n");
2373 void table::compute_vrule_top_adjust(int start_row, int col, string &result)
2375 if (row_is_all_lines[start_row] && start_row < nrows - 1) {
2376 if (row_is_all_lines[start_row] == 2)
2377 result = LINE_SEP ">?\\n[.V]u" "+" DOUBLE_LINE_SEP;
2378 else
2379 result = LINE_SEP ">?\\n[.V]u";
2380 start_row++;
2382 else {
2383 result = "";
2384 if (start_row == 0)
2385 return;
2386 for (stuff *p = stuff_list; p && p->row <= start_row; p = p->next)
2387 if (p->row == start_row
2388 && (p->is_single_line() || p->is_double_line()))
2389 return;
2391 int left = 0;
2392 if (col > 0) {
2393 table_entry *e = entry[start_row-1][col-1];
2394 if (e && e->start_row == e->end_row) {
2395 if (e->to_double_line_entry() != 0)
2396 left = 2;
2397 else if (e->to_single_line_entry() != 0)
2398 left = 1;
2401 int right = 0;
2402 if (col < ncolumns) {
2403 table_entry *e = entry[start_row-1][col];
2404 if (e && e->start_row == e->end_row) {
2405 if (e->to_double_line_entry() != 0)
2406 right = 2;
2407 else if (e->to_single_line_entry() != 0)
2408 right = 1;
2411 if (row_is_all_lines[start_row-1] == 0) {
2412 if (left > 0 || right > 0) {
2413 result += "-" BODY_DEPTH "-" BAR_HEIGHT;
2414 if ((left == 2 && right != 2) || (right == 2 && left != 2))
2415 result += "-" HALF_DOUBLE_LINE_SEP;
2416 else if (left == 2 && right == 2)
2417 result += "+" HALF_DOUBLE_LINE_SEP;
2420 else if (row_is_all_lines[start_row-1] == 2) {
2421 if ((left == 2 && right != 2) || (right == 2 && left != 2))
2422 result += "-" DOUBLE_LINE_SEP;
2423 else if (left == 1 || right == 1)
2424 result += "-" HALF_DOUBLE_LINE_SEP;
2428 void table::compute_vrule_bot_adjust(int end_row, int col, string &result)
2430 if (row_is_all_lines[end_row] && end_row > 0) {
2431 end_row--;
2432 result = "";
2434 else {
2435 stuff *p;
2436 for (p = stuff_list; p && p->row < end_row + 1; p = p->next)
2438 if (p && p->row == end_row + 1 && p->is_double_line()) {
2439 result = "-" DOUBLE_LINE_SEP;
2440 return;
2442 if ((p != 0 && p->row == end_row + 1)
2443 || end_row == nrows - 1) {
2444 result = "";
2445 return;
2447 if (row_is_all_lines[end_row+1] == 1)
2448 result = LINE_SEP;
2449 else if (row_is_all_lines[end_row+1] == 2)
2450 result = LINE_SEP "+" DOUBLE_LINE_SEP;
2451 else
2452 result = "";
2454 int left = 0;
2455 if (col > 0) {
2456 table_entry *e = entry[end_row+1][col-1];
2457 if (e && e->start_row == e->end_row) {
2458 if (e->to_double_line_entry() != 0)
2459 left = 2;
2460 else if (e->to_single_line_entry() != 0)
2461 left = 1;
2464 int right = 0;
2465 if (col < ncolumns) {
2466 table_entry *e = entry[end_row+1][col];
2467 if (e && e->start_row == e->end_row) {
2468 if (e->to_double_line_entry() != 0)
2469 right = 2;
2470 else if (e->to_single_line_entry() != 0)
2471 right = 1;
2474 if (row_is_all_lines[end_row+1] == 0) {
2475 if (left > 0 || right > 0) {
2476 result = "1v-" BODY_DEPTH "-" BAR_HEIGHT;
2477 if ((left == 2 && right != 2) || (right == 2 && left != 2))
2478 result += "+" HALF_DOUBLE_LINE_SEP;
2479 else if (left == 2 && right == 2)
2480 result += "-" HALF_DOUBLE_LINE_SEP;
2483 else if (row_is_all_lines[end_row+1] == 2) {
2484 if (left == 2 && right == 2)
2485 result += "-" DOUBLE_LINE_SEP;
2486 else if (left != 2 && right != 2 && (left == 1 || right == 1))
2487 result += "-" HALF_DOUBLE_LINE_SEP;
2491 void table::add_vertical_rule(int start_row, int end_row,
2492 int col, int is_double)
2494 vrule_list = new vertical_rule(start_row, end_row, col, is_double,
2495 vrule_list);
2496 compute_vrule_top_adjust(start_row, col, vrule_list->top_adjust);
2497 compute_vrule_bot_adjust(end_row, col, vrule_list->bot_adjust);
2500 void table::build_vrule_list()
2502 int col;
2503 if (flags & ALLBOX) {
2504 for (col = 1; col < ncolumns; col++) {
2505 int start_row = 0;
2506 for (;;) {
2507 while (start_row < nrows && vline_spanned(start_row, col))
2508 start_row++;
2509 if (start_row >= nrows)
2510 break;
2511 int end_row = start_row;
2512 while (end_row < nrows && !vline_spanned(end_row, col))
2513 end_row++;
2514 end_row--;
2515 add_vertical_rule(start_row, end_row, col, 0);
2516 start_row = end_row + 1;
2520 if (flags & (BOX | ALLBOX | DOUBLEBOX)) {
2521 add_vertical_rule(0, nrows - 1, 0, 0);
2522 add_vertical_rule(0, nrows - 1, ncolumns, 0);
2524 for (int end_row = 0; end_row < nrows; end_row++)
2525 for (col = 0; col < ncolumns+1; col++)
2526 if (vline[end_row][col] > 0
2527 && !vline_spanned(end_row, col)
2528 && (end_row == nrows - 1
2529 || vline[end_row+1][col] != vline[end_row][col]
2530 || vline_spanned(end_row+1, col))) {
2531 int start_row;
2532 for (start_row = end_row - 1;
2533 start_row >= 0
2534 && vline[start_row][col] == vline[end_row][col]
2535 && !vline_spanned(start_row, col);
2536 start_row--)
2538 start_row++;
2539 add_vertical_rule(start_row, end_row, col, vline[end_row][col] > 1);
2541 for (vertical_rule *p = vrule_list; p; p = p->next)
2542 if (p->is_double)
2543 for (int r = p->start_row; r <= p->end_row; r++) {
2544 if (p->col > 0 && entry[r][p->col-1] != 0
2545 && entry[r][p->col-1]->end_col == p->col-1) {
2546 int is_corner = r == p->start_row || r == p->end_row;
2547 entry[r][p->col-1]->note_double_vrule_on_right(is_corner);
2549 if (p->col < ncolumns && entry[r][p->col] != 0
2550 && entry[r][p->col]->start_col == p->col) {
2551 int is_corner = r == p->start_row || r == p->end_row;
2552 entry[r][p->col]->note_double_vrule_on_left(is_corner);
2557 void table::define_bottom_macro()
2559 prints(".eo\n"
2560 ".de T#\n"
2561 ".if !\\n[" SUPPRESS_BOTTOM_REG "] \\{"
2562 "." REPEATED_VPT_MACRO " 0\n"
2563 ".mk " SAVED_VERTICAL_POS_REG "\n");
2564 if (flags & (BOX | ALLBOX | DOUBLEBOX)) {
2565 prints(".if \\n[T.]&\\n[" NEED_BOTTOM_RULE_REG "] \\{");
2566 print_single_hline(0);
2567 prints(".\\}\n");
2569 prints(".ls 1\n");
2570 for (vertical_rule *p = vrule_list; p; p = p->next)
2571 p->contribute_to_bottom_macro(this);
2572 if (flags & DOUBLEBOX)
2573 prints(".if \\n[T.] \\{.vs " DOUBLE_LINE_SEP ">?\\n[.V]u\n"
2574 "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]"
2575 "\\D'l \\n[TW]u 0'\\s0\n"
2576 ".vs\n"
2577 ".\\}\n"
2578 ".if \\n[" LAST_PASSED_ROW_REG "]>=0 "
2579 ".nr " TOP_REG " \\n[#T]-" DOUBLE_LINE_SEP "\n"
2580 ".sp -1\n"
2581 "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]"
2582 "\\D'l 0 |\\n[" TOP_REG "]u-1v'\\s0\n"
2583 ".sp -1\n"
2584 "\\v'" BODY_DEPTH "'\\h'|\\n[TW]u'\\s[\\n[" LINESIZE_REG "]]"
2585 "\\D'l 0 |\\n[" TOP_REG "]u-1v'\\s0\n");
2586 prints(".ls\n");
2587 prints(".nr " LAST_PASSED_ROW_REG " \\n[" CURRENT_ROW_REG "]\n"
2588 ".sp |\\n[" SAVED_VERTICAL_POS_REG "]u\n"
2589 "." REPEATED_VPT_MACRO " 1\n"
2590 ".\\}\n"
2591 "..\n"
2592 ".ec\n");
2595 // is the vertical line before column c in row r horizontally spanned?
2597 int table::vline_spanned(int r, int c)
2599 assert(r >= 0 && r < nrows && c >= 0 && c < ncolumns + 1);
2600 return (c != 0 && c != ncolumns && entry[r][c] != 0
2601 && entry[r][c]->start_col != c
2602 // horizontally spanning lines don't count
2603 && entry[r][c]->to_double_line_entry() == 0
2604 && entry[r][c]->to_single_line_entry() == 0);
2607 int table::row_begins_section(int r)
2609 assert(r >= 0 && r < nrows);
2610 for (int i = 0; i < ncolumns; i++)
2611 if (entry[r][i] && entry[r][i]->start_row != r)
2612 return 0;
2613 return 1;
2616 int table::row_ends_section(int r)
2618 assert(r >= 0 && r < nrows);
2619 for (int i = 0; i < ncolumns; i++)
2620 if (entry[r][i] && entry[r][i]->end_row != r)
2621 return 0;
2622 return 1;
2625 void table::do_row(int r)
2627 if (!(flags & NOKEEP) && row_begins_section(r))
2628 prints("." KEEP_MACRO_NAME "\n");
2629 int had_line = 0;
2630 stuff *p;
2631 for (p = stuff_list; p && p->row < r; p = p->next)
2633 for (stuff *p1 = p; p1 && p1->row == r; p1 = p1->next)
2634 if (!p1->printed && (p1->is_single_line() || p1->is_double_line())) {
2635 had_line = 1;
2636 break;
2638 if (!had_line && !row_is_all_lines[r])
2639 printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r));
2640 had_line = 0;
2641 for (; p && p->row == r; p = p->next)
2642 if (!p->printed) {
2643 p->print(this);
2644 if (!had_line && (p->is_single_line() || p->is_double_line())) {
2645 printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r));
2646 had_line = 1;
2649 // change the row *after* printing the stuff list (which might contain .TH)
2650 printfs("\\*[" TRANSPARENT_STRING_NAME "].nr " CURRENT_ROW_REG " %1\n",
2651 as_string(r));
2652 if (!had_line && row_is_all_lines[r])
2653 printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r));
2654 // we might have had a .TH, for example, since we last tried
2655 if (!(flags & NOKEEP) && row_begins_section(r))
2656 prints("." KEEP_MACRO_NAME "\n");
2657 printfs(".mk %1\n", row_start_reg(r));
2658 prints(".mk " BOTTOM_REG "\n"
2659 "." REPEATED_VPT_MACRO " 0\n");
2660 int c;
2661 int row_is_blank = 1;
2662 int first_start_row = r;
2663 for (c = 0; c < ncolumns; c++) {
2664 table_entry *e = entry[r][c];
2665 if (e) {
2666 if (e->end_row == r) {
2667 e->do_depth();
2668 if (e->start_row < first_start_row)
2669 first_start_row = e->start_row;
2670 row_is_blank = 0;
2672 c = e->end_col;
2675 if (row_is_blank)
2676 prints(".nr " BOTTOM_REG " +1v\n");
2677 if (row_is_all_lines[r]) {
2678 prints(".vs " LINE_SEP);
2679 if (row_is_all_lines[r] == 2)
2680 prints("+" DOUBLE_LINE_SEP);
2681 prints(">?\\n[.V]u\n.ls 1\n");
2682 prints("\\&");
2683 prints("\\v'" BODY_DEPTH);
2684 if (row_is_all_lines[r] == 2)
2685 prints("-" HALF_DOUBLE_LINE_SEP);
2686 prints("'");
2687 for (c = 0; c < ncolumns; c++) {
2688 table_entry *e = entry[r][c];
2689 if (e) {
2690 if (e->end_row == e->start_row)
2691 e->to_simple_entry()->simple_print(1);
2692 c = e->end_col;
2695 prints("\n");
2696 prints(".ls\n"
2697 ".vs\n");
2698 prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n");
2699 printfs(".sp |\\n[%1]u\n", row_start_reg(r));
2701 for (int i = row_is_all_lines[r] ? r - 1 : r;
2702 i >= first_start_row;
2703 i--) {
2704 simple_entry *first = 0;
2705 for (c = 0; c < ncolumns; c++) {
2706 table_entry *e = entry[r][c];
2707 if (e) {
2708 if (e->end_row == r && e->start_row == i) {
2709 simple_entry *simple = e->to_simple_entry();
2710 if (simple) {
2711 if (!first) {
2712 prints(".ta");
2713 first = simple;
2715 simple->add_tab();
2718 c = e->end_col;
2721 if (first) {
2722 prints('\n');
2723 first->position_vertically();
2724 first->set_location();
2725 prints("\\&");
2726 first->simple_print(0);
2727 for (c = first->end_col + 1; c < ncolumns; c++) {
2728 table_entry *e = entry[r][c];
2729 if (e) {
2730 if (e->end_row == r && e->start_row == i) {
2731 simple_entry *simple = e->to_simple_entry();
2732 if (simple) {
2733 if (e->end_row != e->start_row) {
2734 prints('\n');
2735 simple->position_vertically();
2736 prints("\\&");
2738 simple->simple_print(0);
2741 c = e->end_col;
2744 prints('\n');
2745 prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n");
2746 printfs(".sp |\\n[%1]u\n", row_start_reg(r));
2749 for (c = 0; c < ncolumns; c++) {
2750 table_entry *e = entry[r][c];
2751 if (e) {
2752 if (e->end_row == r && e->to_simple_entry() == 0) {
2753 e->position_vertically();
2754 e->print();
2755 prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n");
2756 printfs(".sp |\\n[%1]u\n", row_start_reg(r));
2758 c = e->end_col;
2761 prints("." REPEATED_VPT_MACRO " 1\n"
2762 ".sp |\\n[" BOTTOM_REG "]u\n"
2763 "\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG " 1\n");
2764 if (r != nrows - 1 && (flags & ALLBOX)) {
2765 print_single_hline(r + 1);
2766 prints("\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG " 0\n");
2768 if (r != nrows - 1) {
2769 if (p && p->row == r + 1
2770 && (p->is_single_line() || p->is_double_line())) {
2771 p->print(this);
2772 prints("\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG
2773 " 0\n");
2775 int printed_one = 0;
2776 for (vertical_rule *vr = vrule_list; vr; vr = vr->next)
2777 if (vr->end_row == r) {
2778 if (!printed_one) {
2779 prints("." REPEATED_VPT_MACRO " 0\n");
2780 printed_one = 1;
2782 vr->print();
2784 if (printed_one)
2785 prints("." REPEATED_VPT_MACRO " 1\n");
2786 if (!(flags & NOKEEP) && row_ends_section(r))
2787 prints("." RELEASE_MACRO_NAME "\n");
2791 void table::do_top()
2793 prints(".fc \002\003\n");
2794 if (!(flags & NOKEEP) && (flags & (BOX | DOUBLEBOX | ALLBOX)))
2795 prints("." TABLE_KEEP_MACRO_NAME "\n");
2796 if (flags & DOUBLEBOX) {
2797 prints(".ls 1\n"
2798 ".vs " LINE_SEP ">?\\n[.V]u\n"
2799 "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]\\D'l \\n[TW]u 0'\\s0\n"
2800 ".vs\n"
2801 "." REPEATED_MARK_MACRO " " TOP_REG "\n"
2802 ".vs " DOUBLE_LINE_SEP ">?\\n[.V]u\n");
2803 printfs("\\v'" BODY_DEPTH "'"
2804 "\\s[\\n[" LINESIZE_REG "]]"
2805 "\\h'\\n[%1]u'"
2806 "\\D'l |\\n[%2]u 0'"
2807 "\\s0"
2808 "\n",
2809 column_divide_reg(0),
2810 column_divide_reg(ncolumns));
2811 prints(".ls\n"
2812 ".vs\n");
2814 else if (flags & (ALLBOX | BOX)) {
2815 print_single_hline(0);
2817 //printfs(".mk %1\n", row_top_reg(0));
2820 void table::do_bottom()
2822 // print stuff after last row
2823 for (stuff *p = stuff_list; p; p = p->next)
2824 if (p->row > nrows - 1)
2825 p->print(this);
2826 if (!(flags & NOKEEP))
2827 prints("." RELEASE_MACRO_NAME "\n");
2828 printfs(".mk %1\n", row_top_reg(nrows));
2829 prints(".nr " NEED_BOTTOM_RULE_REG " 1\n"
2830 ".nr T. 1\n"
2831 ".T#\n");
2832 if (!(flags & NOKEEP) && (flags & (BOX | DOUBLEBOX | ALLBOX)))
2833 prints("." TABLE_RELEASE_MACRO_NAME "\n");
2834 if (flags & DOUBLEBOX)
2835 prints(".sp " DOUBLE_LINE_SEP "\n");
2836 prints("." RESET_MACRO_NAME "\n"
2837 ".fc\n"
2838 ".cp \\n(" COMPATIBLE_REG "\n");
2841 int table::get_nrows()
2843 return nrows;
2846 const char *last_filename = 0;
2848 void set_troff_location(const char *fn, int ln)
2850 if (!location_force_filename && last_filename != 0
2851 && strcmp(fn, last_filename) == 0)
2852 printfs(".lf %1\n", as_string(ln));
2853 else {
2854 printfs(".lf %1 %2\n", as_string(ln), fn);
2855 last_filename = fn;
2856 location_force_filename = 0;
2860 void printfs(const char *s, const string &arg1, const string &arg2,
2861 const string &arg3, const string &arg4, const string &arg5)
2863 if (s) {
2864 char c;
2865 while ((c = *s++) != '\0') {
2866 if (c == '%') {
2867 switch (*s++) {
2868 case '1':
2869 prints(arg1);
2870 break;
2871 case '2':
2872 prints(arg2);
2873 break;
2874 case '3':
2875 prints(arg3);
2876 break;
2877 case '4':
2878 prints(arg4);
2879 break;
2880 case '5':
2881 prints(arg5);
2882 break;
2883 case '6':
2884 case '7':
2885 case '8':
2886 case '9':
2887 break;
2888 case '%':
2889 prints('%');
2890 break;
2891 default:
2892 assert(0);
2895 else
2896 prints(c);