groff before CVS: release 1.07
[s-roff.git] / tbl / table.cc
blob235fe69ff1ff4affe88d78e453c2fa3f19a29b56
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.com)
5 This file is part of groff.
7 groff is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
12 groff is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
17 You should have received a copy of the GNU General Public License along
18 with groff; see the file COPYING. If not, write to the Free Software
19 Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
21 #include "table.h"
23 #define BAR_HEIGHT ".25m"
24 #define DOUBLE_LINE_SEP "2p"
25 #define HALF_DOUBLE_LINE_SEP "1p"
26 #define LINE_SEP "2p"
27 #define BODY_DEPTH ".25m"
29 const int DEFAULT_COLUMN_SEPARATION = 3;
31 #define DELIMITER_CHAR "\\[tbl]"
32 #define PREFIX "3"
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 BLOCK_WIDTH_PREFIX PREFIX "tbw"
67 #define BLOCK_DIVERSION_PREFIX PREFIX "tbd"
68 #define BLOCK_HEIGHT_PREFIX PREFIX "tbh"
69 #define SPAN_WIDTH_PREFIX PREFIX "w"
70 #define SPAN_LEFT_NUMERIC_WIDTH_PREFIX PREFIX "lnw"
71 #define SPAN_RIGHT_NUMERIC_WIDTH_PREFIX PREFIX "rnw"
72 #define SPAN_ALPHABETIC_WIDTH_PREFIX PREFIX "aw"
73 #define COLUMN_SEPARATION_PREFIX PREFIX "cs"
74 #define ROW_START_PREFIX PREFIX "rs"
75 #define COLUMN_START_PREFIX PREFIX "cl"
76 #define COLUMN_END_PREFIX PREFIX "ce"
77 #define COLUMN_DIVIDE_PREFIX PREFIX "cd"
78 #define ROW_TOP_PREFIX PREFIX "rt"
80 string block_width_reg(int r, int c);
81 string block_diversion_name(int r, int c);
82 string block_height_reg(int r, int c);
83 string span_width_reg(int start_col, int end_col);
84 string span_left_numeric_width_reg(int start_col, int end_col);
85 string span_right_numeric_width_reg(int start_col, int end_col);
86 string span_alphabetic_width_reg(int start_col, int end_col);
87 string column_separation_reg(int col);
88 string row_start_reg(int r);
89 string column_start_reg(int c);
90 string column_end_reg(int c);
91 string column_divide_reg(int c);
92 string row_top_reg(int r);
94 void set_inline_modifier(const entry_modifier *);
95 void restore_inline_modifier(const entry_modifier *m);
96 void set_modifier(const entry_modifier *);
97 int find_dot(const char *s, const char *delim);
99 string an_empty_string;
100 int location_force_filename = 0;
102 void printfs(const char *,
103 const string &arg1 = an_empty_string,
104 const string &arg2 = an_empty_string,
105 const string &arg3 = an_empty_string,
106 const string &arg4 = an_empty_string,
107 const string &arg5 = an_empty_string);
109 void prints(const string &);
111 inline void prints(char c)
113 putchar(c);
116 inline void prints(const char *s)
118 fputs(s, stdout);
121 void prints(const string &s)
123 if (!s.empty())
124 fwrite(s.contents(), 1, s.length(), stdout);
127 struct horizontal_span {
128 horizontal_span *next;
129 short start_col;
130 short end_col;
131 horizontal_span(int, int, horizontal_span *);
134 struct single_line_entry;
135 struct double_line_entry;
136 struct simple_entry;
138 class table_entry {
139 friend class table;
140 table_entry *next;
141 int input_lineno;
142 const char *input_filename;
143 protected:
144 short start_row;
145 short start_col;
146 short end_row;
147 short end_col;
148 const entry_modifier *mod;
149 public:
150 void set_location();
151 table_entry(const entry_modifier *);
152 virtual ~table_entry();
153 virtual int divert(int ncols, const string *mw, int *sep);
154 virtual void do_width();
155 virtual void do_depth();
156 virtual void print() = 0;
157 virtual void position_vertically() = 0;
158 virtual single_line_entry *to_single_line_entry();
159 virtual double_line_entry *to_double_line_entry();
160 virtual simple_entry *to_simple_entry();
161 virtual int line_type();
162 virtual void note_double_vrule_on_right(int);
163 virtual void note_double_vrule_on_left(int);
166 class simple_entry : public table_entry {
167 public:
168 simple_entry(const entry_modifier *);
169 void print();
170 void position_vertically();
171 simple_entry *to_simple_entry();
172 virtual void add_tab();
173 virtual void simple_print(int);
176 class empty_entry : public simple_entry {
177 public:
178 empty_entry(const entry_modifier *);
179 int line_type();
182 class text_entry : public simple_entry {
183 protected:
184 char *contents;
185 void print_contents();
186 public:
187 text_entry(char *, const entry_modifier *);
188 ~text_entry();
191 void text_entry::print_contents()
193 set_inline_modifier(mod);
194 prints(contents);
195 restore_inline_modifier(mod);
198 class repeated_char_entry : public text_entry {
199 public:
200 repeated_char_entry(char *s, const entry_modifier *m);
201 void simple_print(int);
204 class simple_text_entry : public text_entry {
205 public:
206 simple_text_entry(char *s, const entry_modifier *m);
207 void do_width();
210 class left_text_entry : public simple_text_entry {
211 public:
212 left_text_entry(char *s, const entry_modifier *m);
213 void simple_print(int);
214 void add_tab();
217 class right_text_entry : public simple_text_entry {
218 public:
219 right_text_entry(char *s, const entry_modifier *m);
220 void simple_print(int);
221 void add_tab();
224 class center_text_entry : public simple_text_entry {
225 public:
226 center_text_entry(char *s, const entry_modifier *m);
227 void simple_print(int);
228 void add_tab();
231 class numeric_text_entry : public text_entry {
232 int dot_pos;
233 public:
234 numeric_text_entry(char *s, const entry_modifier *m, int pos);
235 void do_width();
236 void simple_print(int);
239 class alphabetic_text_entry : public text_entry {
240 public:
241 alphabetic_text_entry(char *s, const entry_modifier *m);
242 void do_width();
243 void simple_print(int);
244 void add_tab();
247 class line_entry : public simple_entry {
248 protected:
249 char double_vrule_on_right;
250 char double_vrule_on_left;
251 public:
252 line_entry(const entry_modifier *);
253 void note_double_vrule_on_right(int);
254 void note_double_vrule_on_left(int);
255 void simple_print(int) = 0;
258 class single_line_entry : public line_entry {
259 public:
260 single_line_entry(const entry_modifier *m);
261 void simple_print(int);
262 single_line_entry *to_single_line_entry();
263 int line_type();
266 class double_line_entry : public line_entry {
267 public:
268 double_line_entry(const entry_modifier *m);
269 void simple_print(int);
270 double_line_entry *to_double_line_entry();
271 int line_type();
274 class short_line_entry : public simple_entry {
275 public:
276 short_line_entry(const entry_modifier *m);
277 void simple_print(int);
278 int line_type();
281 class short_double_line_entry : public simple_entry {
282 public:
283 short_double_line_entry(const entry_modifier *m);
284 void simple_print(int);
285 int line_type();
288 class block_entry : public table_entry {
289 char *contents;
290 protected:
291 void do_divert(int alphabetic, int ncols, const string *mw, int *sep);
292 public:
293 block_entry(char *s, const entry_modifier *m);
294 ~block_entry();
295 int divert(int ncols, const string *mw, int *sep);
296 void do_width();
297 void do_depth();
298 void position_vertically();
299 void print() = 0;
302 class left_block_entry : public block_entry {
303 public:
304 left_block_entry(char *s, const entry_modifier *m);
305 void print();
308 class right_block_entry : public block_entry {
309 public:
310 right_block_entry(char *s, const entry_modifier *m);
311 void print();
314 class center_block_entry : public block_entry {
315 public:
316 center_block_entry(char *s, const entry_modifier *m);
317 void print();
320 class alphabetic_block_entry : public block_entry {
321 public:
322 alphabetic_block_entry(char *s, const entry_modifier *m);
323 void print();
324 int divert(int ncols, const string *mw, int *sep);
327 table_entry::table_entry(const entry_modifier *m)
328 : next(0), start_row(-1), end_row(-1), start_col(-1), end_col(-1), mod(m),
329 input_lineno(-1), input_filename(0)
333 table_entry::~table_entry()
337 int table_entry::divert(int, const string *, int *)
339 return 0;
342 void table_entry::do_width()
346 single_line_entry *table_entry::to_single_line_entry()
348 return 0;
351 double_line_entry *table_entry::to_double_line_entry()
353 return 0;
356 simple_entry *table_entry::to_simple_entry()
358 return 0;
361 void table_entry::do_depth()
365 void table_entry::set_location()
367 set_troff_location(input_filename, input_lineno);
370 int table_entry::line_type()
372 return -1;
375 void table_entry::note_double_vrule_on_right(int)
379 void table_entry::note_double_vrule_on_left(int)
383 simple_entry::simple_entry(const entry_modifier *m) : table_entry(m)
387 void simple_entry::add_tab()
389 // do nothing
392 void simple_entry::simple_print(int)
394 // do nothing
397 void simple_entry::position_vertically()
399 if (start_row != end_row)
400 switch (mod->vertical_alignment) {
401 case entry_modifier::TOP:
402 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
403 break;
404 case entry_modifier::CENTER:
405 // Peform the motion in two stages so that the center is rounded
406 // vertically upwards even if net vertical motion is upwards.
407 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
408 printfs(".sp \\n[" BOTTOM_REG "]u-\\n[%1]u-1v/2u\n",
409 row_start_reg(start_row));
410 break;
411 case entry_modifier::BOTTOM:
412 printfs(".sp |\\n[%1]u+\\n[" BOTTOM_REG "]u-\\n[%1]u-1v\n",
413 row_start_reg(start_row));
414 break;
415 default:
416 assert(0);
420 void simple_entry::print()
422 prints(".ta");
423 add_tab();
424 prints('\n');
425 set_location();
426 prints("\\&");
427 simple_print(0);
428 prints('\n');
431 simple_entry *simple_entry::to_simple_entry()
433 return this;
436 empty_entry::empty_entry(const entry_modifier *m)
437 : simple_entry(m)
441 int empty_entry::line_type()
443 return 0;
446 text_entry::text_entry(char *s, const entry_modifier *m)
447 : contents(s), simple_entry(m)
451 text_entry::~text_entry()
453 a_delete contents;
457 repeated_char_entry::repeated_char_entry(char *s, const entry_modifier *m)
458 : text_entry(s, m)
462 void repeated_char_entry::simple_print(int)
464 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
465 set_inline_modifier(mod);
466 printfs("\\l" DELIMITER_CHAR "\\n[%1]u\\&",
467 span_width_reg(start_col, end_col));
468 prints(contents);
469 prints(DELIMITER_CHAR);
470 restore_inline_modifier(mod);
473 simple_text_entry::simple_text_entry(char *s, const entry_modifier *m)
474 : text_entry(s, m)
478 void simple_text_entry::do_width()
480 set_location();
481 printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR,
482 span_width_reg(start_col, end_col));
483 print_contents();
484 prints(DELIMITER_CHAR "\n");
487 left_text_entry::left_text_entry(char *s, const entry_modifier *m)
488 : simple_text_entry(s, m)
492 void left_text_entry::simple_print(int)
494 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
495 print_contents();
498 // The only point of this is to make `\a' ``work'' as in Unix tbl. Grrr.
500 void left_text_entry::add_tab()
502 printfs(" \\n[%1]u", column_end_reg(end_col));
505 right_text_entry::right_text_entry(char *s, const entry_modifier *m)
506 : simple_text_entry(s, m)
510 void right_text_entry::simple_print(int)
512 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
513 prints("\002\003");
514 print_contents();
515 prints("\002");
518 void right_text_entry::add_tab()
520 printfs(" \\n[%1]u", column_end_reg(end_col));
523 center_text_entry::center_text_entry(char *s, const entry_modifier *m)
524 : simple_text_entry(s, m)
528 void center_text_entry::simple_print(int)
530 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
531 prints("\002\003");
532 print_contents();
533 prints("\003\002");
536 void center_text_entry::add_tab()
538 printfs(" \\n[%1]u", column_end_reg(end_col));
541 numeric_text_entry::numeric_text_entry(char *s, const entry_modifier *m, int pos)
542 : text_entry(s, m), dot_pos(pos)
546 void numeric_text_entry::do_width()
548 if (dot_pos != 0) {
549 set_location();
550 printfs(".nr %1 0\\w" DELIMITER_CHAR,
551 block_width_reg(start_row, start_col));
552 set_inline_modifier(mod);
553 for (int i = 0; i < dot_pos; i++)
554 prints(contents[i]);
555 restore_inline_modifier(mod);
556 prints(DELIMITER_CHAR "\n");
557 printfs(".nr %1 \\n[%1]>?\\n[%2]\n",
558 span_left_numeric_width_reg(start_col, end_col),
559 block_width_reg(start_row, start_col));
561 else
562 printfs(".nr %1 0\n", block_width_reg(start_row, start_col));
563 if (contents[dot_pos] != '\0') {
564 set_location();
565 printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR,
566 span_right_numeric_width_reg(start_col, end_col));
567 set_inline_modifier(mod);
568 prints(contents + dot_pos);
569 restore_inline_modifier(mod);
570 prints(DELIMITER_CHAR "\n");
574 void numeric_text_entry::simple_print(int)
576 printfs("\\h'|(\\n[%1]u-\\n[%2]u-\\n[%3]u/2u+\\n[%2]u+\\n[%4]u-\\n[%5]u)'",
577 span_width_reg(start_col, end_col),
578 span_left_numeric_width_reg(start_col, end_col),
579 span_right_numeric_width_reg(start_col, end_col),
580 column_start_reg(start_col),
581 block_width_reg(start_row, start_col));
582 print_contents();
585 alphabetic_text_entry::alphabetic_text_entry(char *s, const entry_modifier *m)
586 : text_entry(s, m)
590 void alphabetic_text_entry::do_width()
592 set_location();
593 printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR,
594 span_alphabetic_width_reg(start_col, end_col));
595 print_contents();
596 prints(DELIMITER_CHAR "\n");
599 void alphabetic_text_entry::simple_print(int)
601 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
602 printfs("\\h'\\n[%1]u-\\n[%2]u/2u'",
603 span_width_reg(start_col, end_col),
604 span_alphabetic_width_reg(start_col, end_col));
605 print_contents();
608 // The only point of this is to make `\a' ``work'' as in Unix tbl. Grrr.
610 void alphabetic_text_entry::add_tab()
612 printfs(" \\n[%1]u", column_end_reg(end_col));
615 block_entry::block_entry(char *s, const entry_modifier *m)
616 : table_entry(m), contents(s)
620 block_entry::~block_entry()
622 a_delete contents;
625 void block_entry::position_vertically()
627 if (start_row != end_row)
628 switch(mod->vertical_alignment) {
629 case entry_modifier::TOP:
630 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
631 break;
632 case entry_modifier::CENTER:
633 // Peform the motion in two stages so that the center is rounded
634 // vertically upwards even if net vertical motion is upwards.
635 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
636 printfs(".sp \\n[" BOTTOM_REG "]u-\\n[%1]u-\\n[%2]u/2u\n",
637 row_start_reg(start_row),
638 block_height_reg(start_row, start_col));
639 break;
640 case entry_modifier::BOTTOM:
641 printfs(".sp |\\n[%1]u+\\n[" BOTTOM_REG "]u-\\n[%1]u-\\n[%2]u\n",
642 row_start_reg(start_row),
643 block_height_reg(start_row, start_col));
644 break;
645 default:
646 assert(0);
648 if (mod->stagger)
649 prints(".sp -.5v\n");
652 int block_entry::divert(int ncols, const string *mw, int *sep)
654 do_divert(0, ncols, mw, sep);
655 return 1;
658 void block_entry::do_divert(int alphabetic, int ncols, const string *mw,
659 int *sep)
661 printfs(".di %1\n", block_diversion_name(start_row, start_col));
662 prints(".if \\n[" SAVED_FILL_REG "] .fi\n"
663 ".in 0\n");
664 prints(".ll ");
665 for (int i = start_col; i <= end_col; i++)
666 if (mw[i].empty())
667 break;
668 if (i > end_col) {
669 // Every column spanned by this entry has a minimum width.
670 for (int i = start_col; i <= end_col; i++) {
671 if (i > start_col) {
672 if (sep)
673 printfs("+%1n", as_string(sep[i - 1]));
674 prints('+');
676 printfs("(n;%1)", mw[i]);
678 printfs(">?\\n[%1]u", span_width_reg(start_col, end_col));
680 else
681 printfs("(u;\\n[%1]>?(\\n[.l]*%2/%3))",
682 span_width_reg(start_col, end_col),
683 as_string(end_col - start_col + 1),
684 as_string(ncols + 1));
685 if (alphabetic)
686 prints("-2n");
687 prints("\n");
688 set_modifier(mod);
689 prints(".cp \\n(" COMPATIBLE_REG "\n");
690 set_location();
691 prints(contents);
692 prints(".br\n.di\n.cp 0\n");
693 if (!mod->zero_width) {
694 if (alphabetic) {
695 printfs(".nr %1 \\n[%1]>?(\\n[dl]+2n)\n",
696 span_width_reg(start_col, end_col));
697 printfs(".nr %1 \\n[%1]>?\\n[dl]\n",
698 span_alphabetic_width_reg(start_col, end_col));
700 else
701 printfs(".nr %1 \\n[%1]>?\\n[dl]\n", span_width_reg(start_col, end_col));
703 printfs(".nr %1 \\n[dn]\n", block_height_reg(start_row, start_col));
704 printfs(".nr %1 \\n[dl]\n", block_width_reg(start_row, start_col));
705 prints("." RESET_MACRO_NAME "\n"
706 ".in \\n[" SAVED_INDENT_REG "]u\n"
707 ".nf\n");
708 // the block might have contained .lf commands
709 location_force_filename = 1;
712 void block_entry::do_width()
714 // do nothing; the action happens in divert
717 void block_entry::do_depth()
719 printfs(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?(\\n[%1]+\\n[%2])\n",
720 row_start_reg(start_row),
721 block_height_reg(start_row, start_col));
724 left_block_entry::left_block_entry(char *s, const entry_modifier *m)
725 : block_entry(s, m)
729 void left_block_entry::print()
731 printfs(".in +\\n[%1]u\n", column_start_reg(start_col));
732 printfs(".%1\n", block_diversion_name(start_row, start_col));
733 prints(".in\n");
738 right_block_entry::right_block_entry(char *s, const entry_modifier *m)
739 : block_entry(s, m)
743 void right_block_entry::print()
745 printfs(".in +\\n[%1]u+\\n[%2]u-\\n[%3]u\n",
746 column_start_reg(start_col),
747 span_width_reg(start_col, end_col),
748 block_width_reg(start_row, start_col));
749 printfs(".%1\n", block_diversion_name(start_row, start_col));
750 prints(".in\n");
753 center_block_entry::center_block_entry(char *s, const entry_modifier *m)
754 : block_entry(s, m)
758 void center_block_entry::print()
760 printfs(".in +\\n[%1]u+(\\n[%2]u-\\n[%3]u/2u)\n",
761 column_start_reg(start_col),
762 span_width_reg(start_col, end_col),
763 block_width_reg(start_row, start_col));
764 printfs(".%1\n", block_diversion_name(start_row, start_col));
765 prints(".in\n");
768 alphabetic_block_entry::alphabetic_block_entry(char *s,
769 const entry_modifier *m)
770 : block_entry(s, m)
774 int alphabetic_block_entry::divert(int ncols, const string *mw, int *sep)
776 do_divert(1, ncols, mw, sep);
777 return 1;
780 void alphabetic_block_entry::print()
782 printfs(".in +\\n[%1]u+(\\n[%2]u-\\n[%3]u/2u)\n",
783 column_start_reg(start_col),
784 span_width_reg(start_col, end_col),
785 span_alphabetic_width_reg(start_col, end_col));
786 printfs(".%1\n", block_diversion_name(start_row, start_col));
787 prints(".in\n");
790 line_entry::line_entry(const entry_modifier *m)
791 : simple_entry(m), double_vrule_on_right(0), double_vrule_on_left(0)
795 void line_entry::note_double_vrule_on_right(int is_corner)
797 double_vrule_on_right = is_corner ? 1 : 2;
800 void line_entry::note_double_vrule_on_left(int is_corner)
802 double_vrule_on_left = is_corner ? 1 : 2;
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));
971 void set_inline_modifier(const entry_modifier *m)
973 if (!m->font.empty())
974 printfs("\\f[%1]", m->font);
975 if (m->point_size.val != 0) {
976 prints("\\s[");
977 if (m->point_size.inc > 0)
978 prints('+');
979 else if (m->point_size.inc < 0)
980 prints('-');
981 printfs("%1]", as_string(m->point_size.val));
983 if (m->stagger)
984 prints("\\v'-.5v'");
987 void restore_inline_modifier(const entry_modifier *m)
989 if (!m->font.empty())
990 prints("\\f[\\n[" SAVED_FONT_REG "]]");
991 if (m->point_size.val != 0)
992 prints("\\s[\\n[" SAVED_SIZE_REG "]]");
993 if (m->stagger)
994 prints("\\v'.5v'");
998 struct stuff {
999 stuff *next;
1000 int row; // occurs before row `row'
1001 char printed; // has it been printed?
1003 stuff(int);
1004 virtual void print(table *) = 0;
1005 virtual ~stuff();
1006 virtual int is_single_line() { return 0; };
1007 virtual int is_double_line() { return 0; };
1010 stuff::stuff(int r) : row(r), next(0), printed(0)
1014 stuff::~stuff()
1018 struct text_stuff : stuff {
1019 string contents;
1020 const char *filename;
1021 int lineno;
1023 text_stuff(const string &, int r, const char *fn, int ln);
1024 ~text_stuff();
1025 void print(table *);
1029 text_stuff::text_stuff(const string &s, int r, const char *fn, int ln)
1030 : contents(s), stuff(r), 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 : 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 short start_row;
1093 short end_row;
1094 short 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 : start_row(sr), end_row(er), col(c), is_double(dbl), next(p)
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)
1200 : ncolumns(nc), flags(f), linesize(ls),
1201 nrows(0), allocated_rows(0), entry(0), entry_list(0),
1202 left_separation(0), right_separation(0), stuff_list(0), vline(0),
1203 vrule_list(0), row_is_all_lines(0), span_list(0)
1205 minimum_width = new string[ncolumns];
1206 column_separation = ncolumns > 1 ? new int[ncolumns - 1] : 0;
1207 equal = new char[ncolumns];
1208 int i;
1209 for (i = 0; i < ncolumns; i++)
1210 equal[i] = 0;
1211 for (i = 0; i < ncolumns-1; i++)
1212 column_separation[i] = DEFAULT_COLUMN_SEPARATION;
1213 delim[0] = delim[1] = '\0';
1216 table::~table()
1218 for (int i = 0; i < nrows; i++) {
1219 a_delete entry[i];
1220 a_delete vline[i];
1222 a_delete entry;
1223 a_delete vline;
1224 while (entry_list) {
1225 table_entry *tem = entry_list;
1226 entry_list = entry_list->next;
1227 delete tem;
1229 ad_delete(ncolumns) minimum_width;
1230 a_delete column_separation;
1231 a_delete equal;
1232 while (stuff_list) {
1233 stuff *tem = stuff_list;
1234 stuff_list = stuff_list->next;
1235 delete tem;
1237 while (vrule_list) {
1238 vertical_rule *tem = vrule_list;
1239 vrule_list = vrule_list->next;
1240 delete tem;
1242 a_delete row_is_all_lines;
1243 while (span_list) {
1244 horizontal_span *tem = span_list;
1245 span_list = span_list->next;
1246 delete tem;
1250 void table::set_delim(char c1, char c2)
1252 delim[0] = c1;
1253 delim[1] = c2;
1256 void table::set_minimum_width(int c, const string &w)
1258 assert(c >= 0 && c < ncolumns);
1259 minimum_width[c] = w;
1262 void table::set_column_separation(int c, int n)
1264 assert(c >= 0 && c < ncolumns - 1);
1265 column_separation[c] = n;
1268 void table::set_equal_column(int c)
1270 assert(c >= 0 && c < ncolumns);
1271 equal[c] = 1;
1274 void table::add_stuff(stuff *p)
1276 for (stuff **pp = &stuff_list; *pp; pp = &(*pp)->next)
1278 *pp = p;
1281 void table::add_text_line(int r, const string &s, const char *filename, int lineno)
1283 add_stuff(new text_stuff(s, r, filename, lineno));
1286 void table::add_single_hline(int r)
1288 add_stuff(new single_hline_stuff(r));
1291 void table::add_double_hline(int r)
1293 add_stuff(new double_hline_stuff(r));
1296 void table::allocate(int r)
1298 if (r >= nrows) {
1299 typedef table_entry **PPtable_entry; // work around g++ 1.36.1 bug
1300 if (r >= allocated_rows) {
1301 if (allocated_rows == 0) {
1302 allocated_rows = 16;
1303 if (allocated_rows <= r)
1304 allocated_rows = r + 1;
1305 entry = new PPtable_entry[allocated_rows];
1306 vline = new char*[allocated_rows];
1308 else {
1309 table_entry ***old_entry = entry;
1310 int old_allocated_rows = allocated_rows;
1311 allocated_rows *= 2;
1312 if (allocated_rows <= r)
1313 allocated_rows = r + 1;
1314 entry = new PPtable_entry[allocated_rows];
1315 memcpy(entry, old_entry, sizeof(table_entry**)*old_allocated_rows);
1316 a_delete old_entry;
1317 char **old_vline = vline;
1318 vline = new char*[allocated_rows];
1319 memcpy(vline, old_vline, sizeof(char*)*old_allocated_rows);
1320 a_delete old_vline;
1323 assert(allocated_rows > r);
1324 while (nrows <= r) {
1325 entry[nrows] = new table_entry*[ncolumns];
1326 for (int i = 0; i < ncolumns; i++)
1327 entry[nrows][i] = 0;
1328 vline[nrows] = new char[ncolumns+1];
1329 for (i = 0; i < ncolumns+1; i++)
1330 vline[nrows][i] = 0;
1331 nrows++;
1336 void table::do_hspan(int r, int c)
1338 assert(r >= 0 && c >= 0 && r < nrows && c < ncolumns);
1339 if (c == 0) {
1340 error("first column cannot be horizontally spanned");
1341 return;
1343 table_entry *e = entry[r][c];
1344 if (e) {
1345 assert(e->start_row <= r && r <= e->end_row
1346 && e->start_col <= c && c <= e->end_col
1347 && e->end_row - e->start_row > 0
1348 && e->end_col - e->start_col > 0);
1349 return;
1351 e = entry[r][c-1];
1352 // e can be 0 if we had an empty entry or an error
1353 if (e == 0)
1354 return;
1355 if (e->start_row != r) {
1358 ^ s */
1359 error("impossible horizontal span at row %1, column %2", r + 1, c + 1);
1361 else {
1362 e->end_col = c;
1363 entry[r][c] = e;
1367 void table::do_vspan(int r, int c)
1369 assert(r >= 0 && c >= 0 && r < nrows && c < ncolumns);
1370 if (r == 0) {
1371 error("first row cannot be vertically spanned");
1372 return;
1374 table_entry *e = entry[r][c];
1375 if (e) {
1376 assert(e->start_row <= r && r <= e->end_row
1377 && e->start_col <= c && c <= e->end_col
1378 && e->end_row - e->start_row > 0
1379 && e->end_col - e->start_col > 0);
1380 return;
1382 e = entry[r-1][c];
1383 // e can be 0 if we had an empty entry or an error
1384 if (e == 0)
1385 return;
1386 if (e->start_col != c) {
1387 /* l s
1388 l ^ */
1389 error("impossible vertical span at row %1, column %2", r + 1, c + 1);
1391 else {
1392 for (int i = c; i <= e->end_col; i++) {
1393 assert(entry[r][i] == 0);
1394 entry[r][i] = e;
1396 e->end_row = r;
1400 int find_dot(const char *s, const char *delim)
1402 if (s == 0 || *s == '\0')
1403 return -1;
1404 const char *p;
1405 int in_delim = 0; // is p within eqn delimiters?
1406 // tbl recognises \& even within eqn delimiters; I don't
1407 for (p = s; *p; p++)
1408 if (in_delim) {
1409 if (*p == delim[1])
1410 in_delim = 0;
1412 else if (*p == delim[0])
1413 in_delim = 1;
1414 else if (p[0] == '\\' && p[1] == '&')
1415 return p - s;
1416 int possible_pos = -1;
1417 in_delim = 0;
1418 for (p = s; *p; p++)
1419 if (in_delim) {
1420 if (*p == delim[1])
1421 in_delim = 0;
1423 else if (*p == delim[0])
1424 in_delim = 1;
1425 else if (p[0] == '.' && csdigit(p[1]))
1426 possible_pos = p - s;
1427 if (possible_pos >= 0)
1428 return possible_pos;
1429 in_delim = 0;
1430 for (p = s; *p; p++)
1431 if (in_delim) {
1432 if (*p == delim[1])
1433 in_delim = 0;
1435 else if (*p == delim[0])
1436 in_delim = 1;
1437 else if (csdigit(*p))
1438 possible_pos = p + 1 - s;
1439 return possible_pos;
1442 void table::add_entry(int r, int c, const string &str, const entry_format *f,
1443 const char *fn, int ln)
1445 allocate(r);
1446 table_entry *e = 0;
1447 if (str == "\\_") {
1448 e = new short_line_entry(f);
1450 else if (str == "\\=") {
1451 e = new short_double_line_entry(f);
1453 else if (str == "_") {
1454 single_line_entry *lefte;
1455 if (c > 0 && entry[r][c-1] != 0 &&
1456 (lefte = entry[r][c-1]->to_single_line_entry()) != 0
1457 && lefte->start_row == r
1458 && lefte->mod->stagger == f->stagger) {
1459 lefte->end_col = c;
1460 entry[r][c] = lefte;
1462 else
1463 e = new single_line_entry(f);
1465 else if (str == "=") {
1466 double_line_entry *lefte;
1467 if (c > 0 && entry[r][c-1] != 0 &&
1468 (lefte = entry[r][c-1]->to_double_line_entry()) != 0
1469 && lefte->start_row == r
1470 && lefte->mod->stagger == f->stagger) {
1471 lefte->end_col = c;
1472 entry[r][c] = lefte;
1474 else
1475 e = new double_line_entry(f);
1477 else if (str == "\\^") {
1478 do_vspan(r, c);
1480 else if (str.length() > 2 && str[0] == '\\' && str[1] == 'R') {
1481 if (str.search('\n') >= 0)
1482 error_with_file_and_line(fn, ln, "bad repeated character");
1483 else {
1484 char *s = str.substring(2, str.length() - 2).extract();
1485 e = new repeated_char_entry(s, f);
1488 else {
1489 int is_block = str.search('\n') >= 0;
1490 char *s;
1491 switch (f->type) {
1492 case FORMAT_SPAN:
1493 assert(str.empty());
1494 do_hspan(r, c);
1495 break;
1496 case FORMAT_LEFT:
1497 if (!str.empty()) {
1498 s = str.extract();
1499 if (is_block)
1500 e = new left_block_entry(s, f);
1501 else
1502 e = new left_text_entry(s, f);
1504 else
1505 e = new empty_entry(f);
1506 break;
1507 case FORMAT_CENTER:
1508 if (!str.empty()) {
1509 s = str.extract();
1510 if (is_block)
1511 e = new center_block_entry(s, f);
1512 else
1513 e = new center_text_entry(s, f);
1515 else
1516 e = new empty_entry(f);
1517 break;
1518 case FORMAT_RIGHT:
1519 if (!str.empty()) {
1520 s = str.extract();
1521 if (is_block)
1522 e = new right_block_entry(s, f);
1523 else
1524 e = new right_text_entry(s, f);
1526 else
1527 e = new empty_entry(f);
1528 break;
1529 case FORMAT_NUMERIC:
1530 if (!str.empty()) {
1531 s = str.extract();
1532 if (is_block) {
1533 error_with_file_and_line(fn, ln, "can't have numeric text block");
1534 e = new left_block_entry(s, f);
1536 else {
1537 int pos = find_dot(s, delim);
1538 if (pos < 0)
1539 e = new center_text_entry(s, f);
1540 else
1541 e = new numeric_text_entry(s, f, pos);
1544 else
1545 e = new empty_entry(f);
1546 break;
1547 case FORMAT_ALPHABETIC:
1548 if (!str.empty()) {
1549 s = str.extract();
1550 if (is_block)
1551 e = new alphabetic_block_entry(s, f);
1552 else
1553 e = new alphabetic_text_entry(s, f);
1555 else
1556 e = new empty_entry(f);
1557 break;
1558 case FORMAT_VSPAN:
1559 do_vspan(r, c);
1560 break;
1561 case FORMAT_HLINE:
1562 if (str.length() != 0)
1563 error_with_file_and_line(fn, ln,
1564 "non-empty data entry for `_' format ignored");
1565 e = new single_line_entry(f);
1566 break;
1567 case FORMAT_DOUBLE_HLINE:
1568 if (str.length() != 0)
1569 error_with_file_and_line(fn, ln,
1570 "non-empty data entry for `=' format ignored");
1571 e = new double_line_entry(f);
1572 break;
1573 default:
1574 assert(0);
1577 if (e) {
1578 table_entry *preve = entry[r][c];
1579 if (preve) {
1580 /* c s
1581 ^ l */
1582 error_with_file_and_line(fn, ln, "row %1, column %2 already spanned",
1583 r + 1, c + 1);
1584 delete e;
1586 else {
1587 e->input_lineno = ln;
1588 e->input_filename = fn;
1589 e->start_row = e->end_row = r;
1590 e->start_col = e->end_col = c;
1591 for (table_entry **p = &entry_list; *p; p = &(*p)->next)
1593 *p = e;
1594 entry[r][c] = e;
1599 // add vertical lines for row r
1601 void table::add_vlines(int r, const char *v)
1603 allocate(r);
1604 for (int i = 0; i < ncolumns+1; i++)
1605 vline[r][i] = v[i];
1608 void table::check()
1610 table_entry *p = entry_list;
1611 int i, j;
1612 while (p) {
1613 for (i = p->start_row; i <= p->end_row; i++)
1614 for (j = p->start_col; j <= p->end_col; j++)
1615 assert(entry[i][j] == p);
1616 p = p->next;
1620 void table::print()
1622 location_force_filename = 1;
1623 check();
1624 init_output();
1625 determine_row_type();
1626 compute_widths();
1627 if (!(flags & CENTER))
1628 prints(".if \\n[" SAVED_CENTER_REG "] \\{");
1629 prints(".in +(u;\\n[.l]-\\n[.i]-\\n[TW]/2)\n"
1630 ".nr " SAVED_INDENT_REG " \\n[.i]\n");
1631 if (!(flags & CENTER))
1632 prints(".\\}\n");
1633 build_vrule_list();
1634 define_bottom_macro();
1635 do_top();
1636 for (int i = 0; i < nrows; i++)
1637 do_row(i);
1638 do_bottom();
1641 void table::determine_row_type()
1643 row_is_all_lines = new char[nrows];
1644 for (int i = 0; i < nrows; i++) {
1645 int had_single = 0;
1646 int had_double = 0;
1647 int had_non_line = 0;
1648 for (int c = 0; c < ncolumns; c++) {
1649 table_entry *e = entry[i][c];
1650 if (e != 0) {
1651 if (e->start_row == e->end_row) {
1652 int t = e->line_type();
1653 switch (t) {
1654 case -1:
1655 had_non_line = 1;
1656 break;
1657 case 0:
1658 // empty
1659 break;
1660 case 1:
1661 had_single = 1;
1662 break;
1663 case 2:
1664 had_double = 1;
1665 break;
1666 default:
1667 assert(0);
1669 if (had_non_line)
1670 break;
1672 c = e->end_col;
1675 if (had_non_line)
1676 row_is_all_lines[i] = 0;
1677 else if (had_double)
1678 row_is_all_lines[i] = 2;
1679 else if (had_single)
1680 row_is_all_lines[i] = 1;
1681 else
1682 row_is_all_lines[i] = 0;
1687 void table::init_output()
1689 prints(".nr " COMPATIBLE_REG " \\n(.C\n"
1690 ".cp 0\n");
1691 if (linesize > 0)
1692 printfs(".nr " LINESIZE_REG " %1\n", as_string(linesize));
1693 else
1694 prints(".nr " LINESIZE_REG " \\n[.s]\n");
1695 if (!(flags & CENTER))
1696 prints(".nr " SAVED_CENTER_REG " \\n[.ce]\n");
1697 prints(".de " RESET_MACRO_NAME "\n"
1698 ".ft \\n[.f]\n"
1699 ".ps \\n[.s]\n"
1700 ".vs \\n[.v]u\n"
1701 ".in \\n[.i]u\n"
1702 ".ll \\n[.l]u\n"
1703 ".ls \\n[.L]\n"
1704 ".ad \\n[.j]\n"
1705 ".ie \\n[.u] .fi\n"
1706 ".el .nf\n"
1707 ".ce \\n[.ce]\n"
1708 "..\n"
1709 ".nr " SAVED_INDENT_REG " \\n[.i]\n"
1710 ".nr " SAVED_FONT_REG " \\n[.f]\n"
1711 ".nr " SAVED_SIZE_REG " \\n[.s]\n"
1712 ".nr " SAVED_FILL_REG " \\n[.u]\n"
1713 ".nr T. 0\n"
1714 ".nr " CURRENT_ROW_REG " 0-1\n"
1715 ".nr " LAST_PASSED_ROW_REG " 0-1\n"
1716 ".nr " SECTION_DIVERSION_FLAG_REG " 0\n"
1717 ".ds " TRANSPARENT_STRING_NAME "\n"
1718 ".ds " QUOTE_STRING_NAME "\n"
1719 ".nr " NEED_BOTTOM_RULE_REG " 1\n"
1720 ".nr " SUPPRESS_BOTTOM_REG " 0\n"
1721 ".eo\n"
1722 ".de " REPEATED_MARK_MACRO "\n"
1723 ".mk \\$1\n"
1724 ".if !'\\n(.z'' \\!." REPEATED_MARK_MACRO " \"\\$1\"\n"
1725 "..\n"
1726 ".de " REPEATED_VPT_MACRO "\n"
1727 ".vpt \\$1\n"
1728 ".if !'\\n(.z'' \\!." REPEATED_VPT_MACRO " \"\\$1\"\n"
1729 "..\n");
1730 if (!(flags & NOKEEP))
1731 prints(".de " KEEP_MACRO_NAME "\n"
1732 ".if '\\n[.z]'' \\{.ds " QUOTE_STRING_NAME " \\\\\n"
1733 ".ds " TRANSPARENT_STRING_NAME " \\!\n"
1734 ".di " SECTION_DIVERSION_NAME "\n"
1735 ".nr " SECTION_DIVERSION_FLAG_REG " 1\n"
1736 ".in 0\n"
1737 ".\\}\n"
1738 "..\n"
1739 ".de " RELEASE_MACRO_NAME "\n"
1740 ".if \\n[" SECTION_DIVERSION_FLAG_REG "] \\{"
1741 ".di\n"
1742 ".in \\n[" SAVED_INDENT_REG "]u\n"
1743 ".nr " SAVED_DN_REG " \\n[dn]\n"
1744 ".ds " QUOTE_STRING_NAME "\n"
1745 ".ds " TRANSPARENT_STRING_NAME "\n"
1746 ".nr " SECTION_DIVERSION_FLAG_REG " 0\n"
1747 ".if \\n[.t]<=\\n[dn] \\{"
1748 ".nr T. 1\n"
1749 ".T#\n"
1750 ".nr " SUPPRESS_BOTTOM_REG " 1\n"
1751 ".sp \\n[.t]u\n"
1752 ".nr " SUPPRESS_BOTTOM_REG " 0\n"
1753 ".mk #T\n"
1754 ".\\}\n"
1755 ".if \\n[.t]<=\\n[" SAVED_DN_REG "] "
1756 /* Since we turn off traps, it won't get into an infinite loop
1757 when we try and print it; it will just go off the bottom of the
1758 page. */
1759 ".tm warning: page \\n%: table text block will not fit on one page\n"
1760 ".nf\n"
1761 ".ls 1\n"
1762 "." SECTION_DIVERSION_NAME "\n"
1763 ".ls\n"
1764 ".rm " SECTION_DIVERSION_NAME "\n"
1765 ".\\}\n"
1766 "..\n"
1767 ".nr " TABLE_DIVERSION_FLAG_REG " 0\n"
1768 ".de " TABLE_KEEP_MACRO_NAME "\n"
1769 ".if '\\n[.z]'' \\{"
1770 ".di " TABLE_DIVERSION_NAME "\n"
1771 ".nr " TABLE_DIVERSION_FLAG_REG " 1\n"
1772 ".\\}\n"
1773 "..\n"
1774 ".de " TABLE_RELEASE_MACRO_NAME "\n"
1775 ".if \\n[" TABLE_DIVERSION_FLAG_REG "] \\{.br\n"
1776 ".di\n"
1777 ".nr " SAVED_DN_REG " \\n[dn]\n"
1778 ".ne \\n[dn]u+\\n[.V]u\n"
1779 ".ie \\n[.t]<=\\n[" SAVED_DN_REG "] "
1780 ".tm error: page \\n%: table will not fit on one page; use .TS H/.TH with a supporting macro package\n"
1781 ".el \\{"
1782 ".in 0\n"
1783 ".ls 1\n"
1784 ".nf\n"
1785 "." TABLE_DIVERSION_NAME "\n"
1786 ".\\}\n"
1787 ".rm " TABLE_DIVERSION_NAME "\n"
1788 ".\\}\n"
1789 "..\n");
1790 prints(".ec\n"
1791 ".ce 0\n"
1792 ".nf\n");
1795 string block_width_reg(int r, int c)
1797 static char name[sizeof(BLOCK_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1798 sprintf(name, BLOCK_WIDTH_PREFIX "%d,%d", r, c);
1799 return string(name);
1802 string block_diversion_name(int r, int c)
1804 static char name[sizeof(BLOCK_DIVERSION_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1805 sprintf(name, BLOCK_DIVERSION_PREFIX "%d,%d", r, c);
1806 return string(name);
1809 string block_height_reg(int r, int c)
1811 static char name[sizeof(BLOCK_HEIGHT_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1812 sprintf(name, BLOCK_HEIGHT_PREFIX "%d,%d", r, c);
1813 return string(name);
1816 string span_width_reg(int start_col, int end_col)
1818 static char name[sizeof(SPAN_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1819 sprintf(name, SPAN_WIDTH_PREFIX "%d", start_col);
1820 if (end_col != start_col)
1821 sprintf(strchr(name, '\0'), ",%d", end_col);
1822 return string(name);
1825 string span_left_numeric_width_reg(int start_col, int end_col)
1827 static char name[sizeof(SPAN_LEFT_NUMERIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1828 sprintf(name, SPAN_LEFT_NUMERIC_WIDTH_PREFIX "%d", start_col);
1829 if (end_col != start_col)
1830 sprintf(strchr(name, '\0'), ",%d", end_col);
1831 return string(name);
1834 string span_right_numeric_width_reg(int start_col, int end_col)
1836 static char name[sizeof(SPAN_RIGHT_NUMERIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1837 sprintf(name, SPAN_RIGHT_NUMERIC_WIDTH_PREFIX "%d", start_col);
1838 if (end_col != start_col)
1839 sprintf(strchr(name, '\0'), ",%d", end_col);
1840 return string(name);
1843 string span_alphabetic_width_reg(int start_col, int end_col)
1845 static char name[sizeof(SPAN_ALPHABETIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1846 sprintf(name, SPAN_ALPHABETIC_WIDTH_PREFIX "%d", start_col);
1847 if (end_col != start_col)
1848 sprintf(strchr(name, '\0'), ",%d", end_col);
1849 return string(name);
1853 string column_separation_reg(int col)
1855 static char name[sizeof(COLUMN_SEPARATION_PREFIX)+INT_DIGITS];
1856 sprintf(name, COLUMN_SEPARATION_PREFIX "%d", col);
1857 return string(name);
1860 string row_start_reg(int row)
1862 static char name[sizeof(ROW_START_PREFIX)+INT_DIGITS];
1863 sprintf(name, ROW_START_PREFIX "%d", row);
1864 return string(name);
1867 string column_start_reg(int col)
1869 static char name[sizeof(COLUMN_START_PREFIX)+INT_DIGITS];
1870 sprintf(name, COLUMN_START_PREFIX "%d", col);
1871 return string(name);
1874 string column_end_reg(int col)
1876 static char name[sizeof(COLUMN_END_PREFIX)+INT_DIGITS];
1877 sprintf(name, COLUMN_END_PREFIX "%d", col);
1878 return string(name);
1881 string column_divide_reg(int col)
1883 static char name[sizeof(COLUMN_DIVIDE_PREFIX)+INT_DIGITS];
1884 sprintf(name, COLUMN_DIVIDE_PREFIX "%d", col);
1885 return string(name);
1888 string row_top_reg(int row)
1890 static char name[sizeof(ROW_TOP_PREFIX)+INT_DIGITS];
1891 sprintf(name, ROW_TOP_PREFIX "%d", row);
1892 return string(name);
1895 void init_span_reg(int start_col, int end_col)
1897 printfs(".nr %1 \\n(.H\n.nr %2 0\n.nr %3 0\n.nr %4 0\n",
1898 span_width_reg(start_col, end_col),
1899 span_alphabetic_width_reg(start_col, end_col),
1900 span_left_numeric_width_reg(start_col, end_col),
1901 span_right_numeric_width_reg(start_col, end_col));
1904 void compute_span_width(int start_col, int end_col)
1906 printfs(".nr %1 \\n[%1]>?(\\n[%2]+\\n[%3])\n"
1907 ".if \\n[%4] .nr %1 \\n[%1]>?(\\n[%4]+2n)\n",
1908 span_width_reg(start_col, end_col),
1909 span_left_numeric_width_reg(start_col, end_col),
1910 span_right_numeric_width_reg(start_col, end_col),
1911 span_alphabetic_width_reg(start_col, end_col));
1915 // Increase the widths of columns so that the width of any spanning entry
1916 // is no greater than the sum of the widths of the columns that it spans.
1917 // Ensure that the widths of columns remain equal.
1919 void table::divide_span(int start_col, int end_col)
1921 assert(end_col > start_col);
1922 printfs(".nr " NEEDED_REG " \\n[%1]-(\\n[%2]",
1923 span_width_reg(start_col, end_col),
1924 span_width_reg(start_col, start_col));
1925 for (int i = start_col + 1; i <= end_col; i++) {
1926 // The column separation may shrink with the expand option.
1927 if (!(flags & EXPAND))
1928 printfs("+%1n", as_string(column_separation[i - 1]));
1929 printfs("+\\n[%1]", span_width_reg(i, i));
1931 prints(")\n");
1932 printfs(".nr " NEEDED_REG " \\n[" NEEDED_REG "]/%1\n",
1933 as_string(end_col - start_col + 1));
1934 prints(".if \\n[" NEEDED_REG "] \\{");
1935 for (i = start_col; i <= end_col; i++)
1936 printfs(".nr %1 +\\n[" NEEDED_REG "]\n",
1937 span_width_reg(i, i));
1938 int equal_flag = 0;
1939 for (i = start_col; i <= end_col && !equal_flag; i++)
1940 if (equal[i])
1941 equal_flag = 1;
1942 if (equal_flag) {
1943 for (i = 0; i < ncolumns; i++)
1944 if (i < start_col || i > end_col)
1945 printfs(".nr %1 +\\n[" NEEDED_REG "]\n",
1946 span_width_reg(i, i));
1948 prints(".\\}\n");
1952 void table::sum_columns(int start_col, int end_col)
1954 assert(end_col > start_col);
1955 printfs(".nr %1 \\n[%2]",
1956 span_width_reg(start_col, end_col),
1957 span_width_reg(start_col, start_col));
1958 for (int i = start_col + 1; i <= end_col; i++)
1959 printfs("+(%1*\\n[" SEPARATION_FACTOR_REG "])+\\n[%2]",
1960 as_string(column_separation[i - 1]),
1961 span_width_reg(i, i));
1962 prints('\n');
1965 horizontal_span::horizontal_span(int sc, int ec, horizontal_span *p)
1966 : start_col(sc), end_col(ec), next(p)
1970 void table::build_span_list()
1972 span_list = 0;
1973 table_entry *p = entry_list;
1974 while (p) {
1975 if (p->end_col != p->start_col) {
1976 for (horizontal_span *q = span_list; q; q = q->next)
1977 if (q->start_col == p->start_col
1978 && q->end_col == p->end_col)
1979 break;
1980 if (!q)
1981 span_list = new horizontal_span(p->start_col, p->end_col, span_list);
1983 p = p->next;
1985 // Now sort span_list primarily by order of end_row, and secondarily
1986 // by reverse order of start_row. This ensures that if we divide
1987 // spans using the order in span_list, we will get reasonable results.
1988 horizontal_span *unsorted = span_list;
1989 span_list = 0;
1990 while (unsorted) {
1991 for (horizontal_span **pp = &span_list; *pp; pp = &(*pp)->next)
1992 if (unsorted->end_col < (*pp)->end_col
1993 || (unsorted->end_col == (*pp)->end_col
1994 && (unsorted->start_col > (*pp)->start_col)))
1995 break;
1996 horizontal_span *tem = unsorted->next;
1997 unsorted->next = *pp;
1998 *pp = unsorted;
1999 unsorted = tem;
2004 void table::compute_separation_factor()
2006 if (flags & (ALLBOX|BOX|DOUBLEBOX))
2007 left_separation = right_separation = 1;
2008 else {
2009 for (int i = 0; i < nrows; i++) {
2010 if (vline[i][0] > 0)
2011 left_separation = 1;
2012 if (vline[i][ncolumns] > 0)
2013 right_separation = 1;
2016 if (flags & EXPAND) {
2017 int total_sep = left_separation + right_separation;
2018 for (int i = 0; i < ncolumns - 1; i++)
2019 total_sep += column_separation[i];
2020 if (total_sep != 0) {
2021 // Don't let the separation factor be negative.
2022 prints(".nr " SEPARATION_FACTOR_REG " \\n[.l]-\\n[.i]");
2023 for (i = 0; i < ncolumns; i++)
2024 printfs("-\\n[%1]", span_width_reg(i, i));
2025 printfs("/%1>?0\n", as_string(total_sep));
2030 void table::compute_column_positions()
2032 printfs(".nr %1 0\n", column_divide_reg(0));
2033 printfs(".nr %1 %2*\\n[" SEPARATION_FACTOR_REG "]\n",
2034 column_start_reg(0),
2035 as_string(left_separation));
2036 for (int i = 1;; i++) {
2037 printfs(".nr %1 \\n[%2]+\\n[%3]\n",
2038 column_end_reg(i-1),
2039 column_start_reg(i-1),
2040 span_width_reg(i-1, i-1));
2041 if (i >= ncolumns)
2042 break;
2043 printfs(".nr %1 \\n[%2]+(%3*\\n[" SEPARATION_FACTOR_REG "])\n",
2044 column_start_reg(i),
2045 column_end_reg(i-1),
2046 as_string(column_separation[i-1]));
2047 printfs(".nr %1 \\n[%2]+\\n[%3]/2\n",
2048 column_divide_reg(i),
2049 column_end_reg(i-1),
2050 column_start_reg(i));
2052 printfs(".nr %1 \\n[%2]+(%3*\\n[" SEPARATION_FACTOR_REG "])\n",
2053 column_divide_reg(ncolumns),
2054 column_end_reg(i-1),
2055 as_string(right_separation));
2056 printfs(".nr TW \\n[%1]\n",
2057 column_divide_reg(ncolumns));
2058 if (flags & DOUBLEBOX) {
2059 printfs(".nr %1 +" DOUBLE_LINE_SEP "\n", column_divide_reg(0));
2060 printfs(".nr %1 -" DOUBLE_LINE_SEP "\n", column_divide_reg(ncolumns));
2064 void table::make_columns_equal()
2066 int first = -1; // index of first equal column
2067 for (int i = 0; i < ncolumns; i++)
2068 if (equal[i]) {
2069 if (first < 0) {
2070 printfs(".nr %1 \\n[%1]", span_width_reg(i, i));
2071 first = i;
2073 else
2074 printfs(">?\\n[%1]", span_width_reg(i, i));
2076 if (first >= 0) {
2077 prints('\n');
2078 for (i = first + 1; i < ncolumns; i++)
2079 if (equal[i])
2080 printfs(".nr %1 \\n[%2]\n",
2081 span_width_reg(i, i),
2082 span_width_reg(first, first));
2086 void table::compute_widths()
2088 build_span_list();
2089 int i;
2090 horizontal_span *p;
2091 prints(".nr " SEPARATION_FACTOR_REG " 1n\n");
2092 for (i = 0; i < ncolumns; i++) {
2093 init_span_reg(i, i);
2094 if (!minimum_width[i].empty())
2095 printfs(".nr %1 %2\n", span_width_reg(i, i), minimum_width[i]);
2097 for (p = span_list; p; p = p->next)
2098 init_span_reg(p->start_col, p->end_col);
2099 table_entry *q;
2100 for (q = entry_list; q; q = q->next)
2101 if (!q->mod->zero_width)
2102 q->do_width();
2103 for (i = 0; i < ncolumns; i++)
2104 compute_span_width(i, i);
2105 for (p = span_list; p; p = p->next)
2106 compute_span_width(p->start_col, p->end_col);
2107 make_columns_equal();
2108 // Note that divide_span keeps equal width columns equal.
2109 for (p = span_list; p; p = p->next)
2110 divide_span(p->start_col, p->end_col);
2111 for (p = span_list; p; p = p->next)
2112 sum_columns(p->start_col, p->end_col);
2113 int had_spanning_block = 0;
2114 int had_equal_block = 0;
2115 for (q = entry_list; q; q = q->next)
2116 if (q->divert(ncolumns, minimum_width,
2117 (flags & EXPAND) ? column_separation : 0)) {
2118 if (q->end_col > q->start_col)
2119 had_spanning_block = 1;
2120 for (i = q->start_col; i <= q->end_col && !had_equal_block; i++)
2121 if (equal[i])
2122 had_equal_block = 1;
2124 if (had_equal_block)
2125 make_columns_equal();
2126 if (had_spanning_block)
2127 for (p = span_list; p; p = p->next)
2128 divide_span(p->start_col, p->end_col);
2129 compute_separation_factor();
2130 for (p = span_list; p; p = p->next)
2131 sum_columns(p->start_col, p->end_col);
2132 compute_column_positions();
2135 void table::print_single_hline(int r)
2137 prints(".vs " LINE_SEP ">?\\n[.V]u\n"
2138 ".ls 1\n"
2139 "\\v'" BODY_DEPTH "'"
2140 "\\s[\\n[" LINESIZE_REG "]]");
2141 if (r > nrows - 1)
2142 prints("\\D'l |\\n[TW]u 0'");
2143 else {
2144 int start_col = 0;
2145 for (;;) {
2146 while (start_col < ncolumns
2147 && entry[r][start_col] != 0
2148 && entry[r][start_col]->start_row != r)
2149 start_col++;
2150 for (int end_col = start_col;
2151 end_col < ncolumns
2152 && (entry[r][end_col] == 0
2153 || entry[r][end_col]->start_row == r);
2154 end_col++)
2156 if (end_col <= start_col)
2157 break;
2158 printfs("\\h'|\\n[%1]u",
2159 column_divide_reg(start_col));
2160 if ((r > 0 && vline[r-1][start_col] == 2)
2161 || (r < nrows && vline[r][start_col] == 2))
2162 prints("-" HALF_DOUBLE_LINE_SEP);
2163 prints("'");
2164 printfs("\\D'l |\\n[%1]u",
2165 column_divide_reg(end_col));
2166 if ((r > 0 && vline[r-1][end_col] == 2)
2167 || (r < nrows && vline[r][end_col] == 2))
2168 prints("+" HALF_DOUBLE_LINE_SEP);
2169 prints(" 0'");
2170 start_col = end_col;
2173 prints("\\s0\n");
2174 prints(".ls\n"
2175 ".vs\n");
2178 void table::print_double_hline(int r)
2180 prints(".vs " LINE_SEP "+" DOUBLE_LINE_SEP
2181 ">?\\n[.V]u\n"
2182 ".ls 1\n"
2183 "\\v'" BODY_DEPTH "'"
2184 "\\s[\\n[" LINESIZE_REG "]]");
2185 if (r > nrows - 1)
2186 prints("\\v'-" DOUBLE_LINE_SEP "'"
2187 "\\D'l |\\n[TW]u 0'"
2188 "\\v'" DOUBLE_LINE_SEP "'"
2189 "\\h'|0'"
2190 "\\D'l |\\n[TW]u 0'");
2191 else {
2192 int start_col = 0;
2193 for (;;) {
2194 while (start_col < ncolumns
2195 && entry[r][start_col] != 0
2196 && entry[r][start_col]->start_row != r)
2197 start_col++;
2198 for (int end_col = start_col;
2199 end_col < ncolumns
2200 && (entry[r][end_col] == 0
2201 || entry[r][end_col]->start_row == r);
2202 end_col++)
2204 if (end_col <= start_col)
2205 break;
2206 const char *left_adjust = 0;
2207 if ((r > 0 && vline[r-1][start_col] == 2)
2208 || (r < nrows && vline[r][start_col] == 2))
2209 left_adjust = "-" HALF_DOUBLE_LINE_SEP;
2210 const char *right_adjust = 0;
2211 if ((r > 0 && vline[r-1][end_col] == 2)
2212 || (r < nrows && vline[r][end_col] == 2))
2213 right_adjust = "+" HALF_DOUBLE_LINE_SEP;
2214 printfs("\\v'-" DOUBLE_LINE_SEP "'"
2215 "\\h'|\\n[%1]u",
2216 column_divide_reg(start_col));
2217 if (left_adjust)
2218 prints(left_adjust);
2219 prints("'");
2220 printfs("\\D'l |\\n[%1]u",
2221 column_divide_reg(end_col));
2222 if (right_adjust)
2223 prints(right_adjust);
2224 prints(" 0'");
2225 printfs("\\v'" DOUBLE_LINE_SEP "'"
2226 "\\h'|\\n[%1]u",
2227 column_divide_reg(start_col));
2228 if (left_adjust)
2229 prints(left_adjust);
2230 prints("'");
2231 printfs("\\D'l |\\n[%1]u",
2232 column_divide_reg(end_col));
2233 if (right_adjust)
2234 prints(right_adjust);
2235 prints(" 0'");
2236 start_col = end_col;
2239 prints("\\s0\n"
2240 ".ls\n"
2241 ".vs\n");
2244 void table::compute_vrule_top_adjust(int start_row, int col, string &result)
2246 if (row_is_all_lines[start_row] && start_row < nrows - 1) {
2247 if (row_is_all_lines[start_row] == 2)
2248 result = LINE_SEP ">?\\n[.V]u" "+" DOUBLE_LINE_SEP;
2249 else
2250 result = LINE_SEP ">?\\n[.V]u";
2251 start_row++;
2253 else {
2254 result = "";
2255 if (start_row == 0)
2256 return;
2257 for (stuff *p = stuff_list; p && p->row <= start_row; p = p->next)
2258 if (p->row == start_row
2259 && (p->is_single_line() || p->is_double_line()))
2260 return;
2262 int left = 0;
2263 if (col > 0) {
2264 table_entry *e = entry[start_row-1][col-1];
2265 if (e && e->start_row == e->end_row) {
2266 if (e->to_double_line_entry() != 0)
2267 left = 2;
2268 else if (e->to_single_line_entry() != 0)
2269 left = 1;
2272 int right = 0;
2273 if (col < ncolumns) {
2274 table_entry *e = entry[start_row-1][col];
2275 if (e && e->start_row == e->end_row) {
2276 if (e->to_double_line_entry() != 0)
2277 right = 2;
2278 else if (e->to_single_line_entry() != 0)
2279 right = 1;
2282 if (row_is_all_lines[start_row-1] == 0) {
2283 if (left > 0 || right > 0) {
2284 result += "-" BODY_DEPTH "-" BAR_HEIGHT;
2285 if ((left == 2 && right != 2) || (right == 2 && left != 2))
2286 result += "-" HALF_DOUBLE_LINE_SEP;
2287 else if (left == 2 && right == 2)
2288 result += "+" HALF_DOUBLE_LINE_SEP;
2291 else if (row_is_all_lines[start_row-1] == 2) {
2292 if ((left == 2 && right != 2) || (right == 2 && left != 2))
2293 result += "-" DOUBLE_LINE_SEP;
2294 else if (left == 1 || right == 1)
2295 result += "-" HALF_DOUBLE_LINE_SEP;
2299 void table::compute_vrule_bot_adjust(int end_row, int col, string &result)
2301 if (row_is_all_lines[end_row] && end_row > 0) {
2302 end_row--;
2303 result = "";
2305 else {
2306 for (stuff *p = stuff_list; p && p->row < end_row + 1; p = p->next)
2308 if (p && p->row == end_row + 1 && p->is_double_line()) {
2309 result = "-" DOUBLE_LINE_SEP;
2310 return;
2312 if ((p != 0 && p->row == end_row + 1)
2313 || end_row == nrows - 1) {
2314 result = "";
2315 return;
2317 if (row_is_all_lines[end_row+1] == 1)
2318 result = LINE_SEP;
2319 else if (row_is_all_lines[end_row+1] == 2)
2320 result = LINE_SEP "+" DOUBLE_LINE_SEP;
2321 else
2322 result = "";
2324 int left = 0;
2325 if (col > 0) {
2326 table_entry *e = entry[end_row+1][col-1];
2327 if (e && e->start_row == e->end_row) {
2328 if (e->to_double_line_entry() != 0)
2329 left = 2;
2330 else if (e->to_single_line_entry() != 0)
2331 left = 1;
2334 int right = 0;
2335 if (col < ncolumns) {
2336 table_entry *e = entry[end_row+1][col];
2337 if (e && e->start_row == e->end_row) {
2338 if (e->to_double_line_entry() != 0)
2339 right = 2;
2340 else if (e->to_single_line_entry() != 0)
2341 right = 1;
2344 if (row_is_all_lines[end_row+1] == 0) {
2345 if (left > 0 || right > 0) {
2346 result = "1v-" BODY_DEPTH "-" BAR_HEIGHT;
2347 if ((left == 2 && right != 2) || (right == 2 && left != 2))
2348 result += "+" HALF_DOUBLE_LINE_SEP;
2349 else if (left == 2 && right == 2)
2350 result += "-" HALF_DOUBLE_LINE_SEP;
2353 else if (row_is_all_lines[end_row+1] == 2) {
2354 if (left == 2 && right == 2)
2355 result += "-" DOUBLE_LINE_SEP;
2356 else if (left != 2 && right != 2 && (left == 1 || right == 1))
2357 result += "-" HALF_DOUBLE_LINE_SEP;
2361 void table::add_vertical_rule(int start_row, int end_row, int col, int is_double)
2363 vrule_list = new vertical_rule(start_row, end_row, col, is_double,
2364 vrule_list);
2365 compute_vrule_top_adjust(start_row, col, vrule_list->top_adjust);
2366 compute_vrule_bot_adjust(end_row, col, vrule_list->bot_adjust);
2369 void table::build_vrule_list()
2371 int col;
2372 if (flags & ALLBOX) {
2373 for (col = 1; col < ncolumns; col++) {
2374 int start_row = 0;
2375 for (;;) {
2376 while (start_row < nrows && vline_spanned(start_row, col))
2377 start_row++;
2378 if (start_row >= nrows)
2379 break;
2380 int end_row = start_row;
2381 while (end_row < nrows && !vline_spanned(end_row, col))
2382 end_row++;
2383 end_row--;
2384 add_vertical_rule(start_row, end_row, col, 0);
2385 start_row = end_row + 1;
2389 if (flags & (BOX|ALLBOX|DOUBLEBOX)) {
2390 add_vertical_rule(0, nrows - 1, 0, 0);
2391 add_vertical_rule(0, nrows - 1, ncolumns, 0);
2393 for (int end_row = 0; end_row < nrows; end_row++)
2394 for (col = 0; col < ncolumns+1; col++)
2395 if (vline[end_row][col] > 0
2396 && !vline_spanned(end_row, col)
2397 && (end_row == nrows - 1
2398 || vline[end_row+1][col] != vline[end_row][col]
2399 || vline_spanned(end_row+1, col))) {
2400 for (int start_row = end_row - 1;
2401 start_row >= 0
2402 && vline[start_row][col] == vline[end_row][col]
2403 && !vline_spanned(start_row, col);
2404 start_row--)
2406 start_row++;
2407 add_vertical_rule(start_row, end_row, col, vline[end_row][col] > 1);
2409 for (vertical_rule *p = vrule_list; p; p = p->next)
2410 if (p->is_double)
2411 for (int r = p->start_row; r <= p->end_row; r++) {
2412 if (p->col > 0 && entry[r][p->col-1] != 0
2413 && entry[r][p->col-1]->end_col == p->col-1) {
2414 int is_corner = r == p->start_row || r == p->end_row;
2415 entry[r][p->col-1]->note_double_vrule_on_right(is_corner);
2417 if (p->col < ncolumns && entry[r][p->col] != 0
2418 && entry[r][p->col]->start_col == p->col) {
2419 int is_corner = r == p->start_row || r == p->end_row;
2420 entry[r][p->col]->note_double_vrule_on_left(is_corner);
2425 void table::define_bottom_macro()
2427 prints(".eo\n"
2428 ".de T#\n"
2429 ".if !\\n[" SUPPRESS_BOTTOM_REG "] \\{"
2430 "." REPEATED_VPT_MACRO " 0\n"
2431 ".mk " SAVED_VERTICAL_POS_REG "\n");
2432 if (flags & (BOX|ALLBOX|DOUBLEBOX)) {
2433 prints(".if \\n[T.]&\\n[" NEED_BOTTOM_RULE_REG "] \\{");
2434 print_single_hline(0);
2435 prints(".\\}\n");
2437 prints(".ls 1\n");
2438 for (vertical_rule *p = vrule_list; p; p = p->next)
2439 p->contribute_to_bottom_macro(this);
2440 if (flags & DOUBLEBOX)
2441 prints(".if \\n[T.] \\{.vs " DOUBLE_LINE_SEP ">?\\n[.V]u\n"
2442 "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]"
2443 "\\D'l \\n[TW]u 0'\\s0\n"
2444 ".vs\n"
2445 ".\\}\n"
2446 ".if \\n[" LAST_PASSED_ROW_REG "]>=0 "
2447 ".nr " TOP_REG " \\n[#T]-" DOUBLE_LINE_SEP "\n"
2448 ".sp -1\n"
2449 "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]"
2450 "\\D'l 0 |\\n[" TOP_REG "]u-1v'\\s0\n"
2451 ".sp -1\n"
2452 "\\v'" BODY_DEPTH "'\\h'|\\n[TW]u'\\s[\\n[" LINESIZE_REG "]]"
2453 "\\D'l 0 |\\n[" TOP_REG "]u-1v'\\s0\n");
2454 prints(".ls\n");
2455 prints(".nr " LAST_PASSED_ROW_REG " \\n[" CURRENT_ROW_REG "]\n"
2456 ".sp |\\n[" SAVED_VERTICAL_POS_REG "]u\n"
2457 "." REPEATED_VPT_MACRO " 1\n"
2458 ".\\}\n"
2459 "..\n"
2460 ".ec\n");
2464 // is the vertical line before column c in row r horizontally spanned?
2466 int table::vline_spanned(int r, int c)
2468 assert(r >= 0 && r < nrows && c >= 0 && c < ncolumns + 1);
2469 return (c != 0 && c != ncolumns && entry[r][c] != 0
2470 && entry[r][c]->start_col != c
2471 // horizontally spanning lines don't count
2472 && entry[r][c]->to_double_line_entry() == 0
2473 && entry[r][c]->to_single_line_entry() == 0);
2476 int table::row_begins_section(int r)
2478 assert(r >= 0 && r < nrows);
2479 for (int i = 0; i < ncolumns; i++)
2480 if (entry[r][i] && entry[r][i]->start_row != r)
2481 return 0;
2482 return 1;
2485 int table::row_ends_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]->end_row != r)
2490 return 0;
2491 return 1;
2494 void table::do_row(int r)
2496 if (!(flags & NOKEEP) && row_begins_section(r))
2497 prints("." KEEP_MACRO_NAME "\n");
2498 int had_line = 0;
2499 for (stuff *p = stuff_list; p && p->row < r; p = p->next)
2501 for (stuff *p1 = p; p1 && p1->row == r; p1 = p1->next)
2502 if (!p1->printed && (p1->is_single_line() || p1->is_double_line())) {
2503 had_line = 1;
2504 break;
2506 if (!had_line && !row_is_all_lines[r])
2507 printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r));
2508 had_line = 0;
2509 for (; p && p->row == r; p = p->next)
2510 if (!p->printed) {
2511 p->print(this);
2512 if (!had_line && (p->is_single_line() || p->is_double_line())) {
2513 printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r));
2514 had_line = 1;
2517 // Change the row *after* printing the stuff list (which might contain .TH).
2518 printfs("\\*[" TRANSPARENT_STRING_NAME "].nr " CURRENT_ROW_REG " %1\n",
2519 as_string(r));
2520 if (!had_line && row_is_all_lines[r])
2521 printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r));
2522 // we might have had a .TH, for example, since we last tried
2523 if (!(flags & NOKEEP) && row_begins_section(r))
2524 prints("." KEEP_MACRO_NAME "\n");
2525 printfs(".mk %1\n", row_start_reg(r));
2526 prints(".mk " BOTTOM_REG "\n"
2527 "." REPEATED_VPT_MACRO " 0\n");
2528 int c;
2529 int row_is_blank = 1;
2530 int first_start_row = r;
2531 for (c = 0; c < ncolumns; c++) {
2532 table_entry *e = entry[r][c];
2533 if (e) {
2534 if (e->end_row == r) {
2535 e->do_depth();
2536 if (e->start_row < first_start_row)
2537 first_start_row = e->start_row;
2538 row_is_blank = 0;
2540 c = e->end_col;
2543 if (row_is_blank)
2544 prints(".nr " BOTTOM_REG " +1v\n");
2545 if (row_is_all_lines[r]) {
2546 prints(".vs " LINE_SEP);
2547 if (row_is_all_lines[r] == 2)
2548 prints("+" DOUBLE_LINE_SEP);
2549 prints(">?\\n[.V]u\n.ls 1\n");
2550 prints("\\&");
2551 prints("\\v'" BODY_DEPTH);
2552 if (row_is_all_lines[r] == 2)
2553 prints("-" HALF_DOUBLE_LINE_SEP);
2554 prints("'");
2555 for (c = 0; c < ncolumns; c++) {
2556 table_entry *e = entry[r][c];
2557 if (e) {
2558 if (e->end_row == e->start_row)
2559 e->to_simple_entry()->simple_print(1);
2560 c = e->end_col;
2563 prints("\n");
2564 prints(".ls\n"
2565 ".vs\n");
2566 prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n");
2567 printfs(".sp |\\n[%1]u\n", row_start_reg(r));
2569 for (int i = row_is_all_lines[r] ? r - 1 : r;
2570 i >= first_start_row;
2571 i--) {
2572 simple_entry *first = 0;
2573 for (c = 0; c < ncolumns; c++) {
2574 table_entry *e = entry[r][c];
2575 if (e) {
2576 if (e->end_row == r && e->start_row == i) {
2577 simple_entry *simple = e->to_simple_entry();
2578 if (simple) {
2579 if (!first) {
2580 prints(".ta");
2581 first = simple;
2583 simple->add_tab();
2586 c = e->end_col;
2589 if (first) {
2590 prints('\n');
2591 first->position_vertically();
2592 first->set_location();
2593 prints("\\&");
2594 first->simple_print(0);
2595 for (c = first->end_col + 1; c < ncolumns; c++) {
2596 table_entry *e = entry[r][c];
2597 if (e) {
2598 if (e->end_row == r && e->start_row == i) {
2599 simple_entry *simple = e->to_simple_entry();
2600 if (simple)
2601 simple->simple_print(0);
2603 c = e->end_col;
2606 prints('\n');
2607 prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n");
2608 printfs(".sp |\\n[%1]u\n", row_start_reg(r));
2611 for (c = 0; c < ncolumns; c++) {
2612 table_entry *e = entry[r][c];
2613 if (e) {
2614 if (e->end_row == r && e->to_simple_entry() == 0) {
2615 e->position_vertically();
2616 e->print();
2617 prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n");
2618 printfs(".sp |\\n[%1]u\n", row_start_reg(r));
2620 c = e->end_col;
2623 prints("." REPEATED_VPT_MACRO " 1\n"
2624 ".sp |\\n[" BOTTOM_REG "]u\n"
2625 "\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG " 1\n");
2626 if (r != nrows - 1 && (flags & ALLBOX)) {
2627 print_single_hline(r + 1);
2628 prints("\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG " 0\n");
2630 if (r != nrows - 1) {
2631 if (p && p->row == r + 1
2632 && (p->is_single_line() || p->is_double_line())) {
2633 p->print(this);
2634 prints("\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG
2635 " 0\n");
2637 int printed_one = 0;
2638 for (vertical_rule *p = vrule_list; p; p = p->next)
2639 if (p->end_row == r) {
2640 if (!printed_one) {
2641 prints("." REPEATED_VPT_MACRO " 0\n");
2642 printed_one = 1;
2644 p->print();
2646 if (printed_one)
2647 prints("." REPEATED_VPT_MACRO " 1\n");
2648 if (!(flags & NOKEEP) && row_ends_section(r))
2649 prints("." RELEASE_MACRO_NAME "\n");
2653 void table::do_top()
2655 prints(".fc \002\003\n");
2656 if (!(flags & NOKEEP) && (flags & (BOX|DOUBLEBOX|ALLBOX)))
2657 prints("." TABLE_KEEP_MACRO_NAME "\n");
2658 if (flags & DOUBLEBOX) {
2659 prints(".ls 1\n"
2660 ".vs " LINE_SEP ">?\\n[.V]u\n"
2661 "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]\\D'l \\n[TW]u 0'\\s0\n"
2662 ".vs\n"
2663 "." REPEATED_MARK_MACRO " " TOP_REG "\n"
2664 ".vs " DOUBLE_LINE_SEP ">?\\n[.V]u\n");
2665 printfs("\\v'" BODY_DEPTH "'"
2666 "\\s[\\n[" LINESIZE_REG "]]"
2667 "\\h'\\n[%1]u'"
2668 "\\D'l |\\n[%2]u 0'"
2669 "\\s0"
2670 "\n",
2671 column_divide_reg(0),
2672 column_divide_reg(ncolumns));
2673 prints(".ls\n"
2674 ".vs\n");
2676 else if (flags & (ALLBOX|BOX)) {
2677 print_single_hline(0);
2679 //printfs(".mk %1\n", row_top_reg(0));
2682 void table::do_bottom()
2684 // print stuff after last row
2685 for (stuff *p = stuff_list; p; p = p->next)
2686 if (p->row > nrows - 1)
2687 p->print(this);
2688 if (!(flags & NOKEEP))
2689 prints("." RELEASE_MACRO_NAME "\n");
2690 printfs(".mk %1\n", row_top_reg(nrows));
2691 prints(".nr " NEED_BOTTOM_RULE_REG " 1\n"
2692 ".nr T. 1\n"
2693 ".T#\n");
2694 if (!(flags & NOKEEP) && (flags & (BOX|DOUBLEBOX|ALLBOX)))
2695 prints("." TABLE_RELEASE_MACRO_NAME "\n");
2696 if (flags & DOUBLEBOX)
2697 prints(".sp " DOUBLE_LINE_SEP "\n");
2698 prints("." RESET_MACRO_NAME "\n"
2699 ".fc\n"
2700 ".cp \\n(" COMPATIBLE_REG "\n");
2703 int table::get_nrows()
2705 return nrows;
2708 const char *last_filename = 0;
2710 void set_troff_location(const char *fn, int ln)
2712 if (!location_force_filename && last_filename != 0
2713 && strcmp(fn, last_filename) == 0)
2714 printfs(".lf %1\n", as_string(ln));
2715 else {
2716 printfs(".lf %1 %2\n", as_string(ln), fn);
2717 last_filename = fn;
2718 location_force_filename = 0;
2722 void printfs(const char *s, const string &arg1, const string &arg2,
2723 const string &arg3, const string &arg4, const string &arg5)
2725 if (s) {
2726 char c;
2727 while ((c = *s++) != '\0') {
2728 if (c == '%') {
2729 switch (*s++) {
2730 case '1':
2731 prints(arg1);
2732 break;
2733 case '2':
2734 prints(arg2);
2735 break;
2736 case '3':
2737 prints(arg3);
2738 break;
2739 case '4':
2740 prints(arg4);
2741 break;
2742 case '5':
2743 prints(arg5);
2744 break;
2745 case '6':
2746 case '7':
2747 case '8':
2748 case '9':
2749 break;
2750 case '%':
2751 prints('%');
2752 break;
2753 default:
2754 assert(0);
2757 else
2758 prints(c);