No empty .Rs/.Re
[netbsd-mini2440.git] / gnu / dist / groff / src / preproc / tbl / table.cpp
blob9a5b77e8116e92707a941f15bdddaaa45fc82ca3
1 /* $NetBSD$ */
3 // -*- C++ -*-
4 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2003, 2004
5 Free Software Foundation, Inc.
6 Written by James Clark (jjc@jclark.com)
8 This file is part of groff.
10 groff is free software; you can redistribute it and/or modify it under
11 the terms of the GNU General Public License as published by the Free
12 Software Foundation; either version 2, or (at your option) any later
13 version.
15 groff is distributed in the hope that it will be useful, but WITHOUT ANY
16 WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 for more details.
20 You should have received a copy of the GNU General Public License along
21 with groff; see the file COPYING. If not, write to the Free Software
22 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
24 #include "table.h"
26 #define BAR_HEIGHT ".25m"
27 #define DOUBLE_LINE_SEP "2p"
28 #define HALF_DOUBLE_LINE_SEP "1p"
29 #define LINE_SEP "2p"
30 #define BODY_DEPTH ".25m"
32 const int DEFAULT_COLUMN_SEPARATION = 3;
34 #define DELIMITER_CHAR "\\[tbl]"
35 #define SEPARATION_FACTOR_REG PREFIX "sep"
36 #define BOTTOM_REG PREFIX "bot"
37 #define RESET_MACRO_NAME PREFIX "init"
38 #define LINESIZE_REG PREFIX "lps"
39 #define TOP_REG PREFIX "top"
40 #define CURRENT_ROW_REG PREFIX "crow"
41 #define LAST_PASSED_ROW_REG PREFIX "passed"
42 #define TRANSPARENT_STRING_NAME PREFIX "trans"
43 #define QUOTE_STRING_NAME PREFIX "quote"
44 #define SECTION_DIVERSION_NAME PREFIX "section"
45 #define SECTION_DIVERSION_FLAG_REG PREFIX "sflag"
46 #define SAVED_VERTICAL_POS_REG PREFIX "vert"
47 #define NEED_BOTTOM_RULE_REG PREFIX "brule"
48 #define KEEP_MACRO_NAME PREFIX "keep"
49 #define RELEASE_MACRO_NAME PREFIX "release"
50 #define SAVED_FONT_REG PREFIX "fnt"
51 #define SAVED_SIZE_REG PREFIX "sz"
52 #define SAVED_FILL_REG PREFIX "fll"
53 #define SAVED_INDENT_REG PREFIX "ind"
54 #define SAVED_CENTER_REG PREFIX "cent"
55 #define TABLE_DIVERSION_NAME PREFIX "table"
56 #define TABLE_DIVERSION_FLAG_REG PREFIX "tflag"
57 #define TABLE_KEEP_MACRO_NAME PREFIX "tkeep"
58 #define TABLE_RELEASE_MACRO_NAME PREFIX "trelease"
59 #define NEEDED_REG PREFIX "needed"
60 #define REPEATED_MARK_MACRO PREFIX "rmk"
61 #define REPEATED_VPT_MACRO PREFIX "rvpt"
62 #define SUPPRESS_BOTTOM_REG PREFIX "supbot"
63 #define SAVED_DN_REG PREFIX "dn"
65 // this must be one character
66 #define COMPATIBLE_REG PREFIX "c"
68 #define LEADER_REG PREFIX LEADER
70 #define BLOCK_WIDTH_PREFIX PREFIX "tbw"
71 #define BLOCK_DIVERSION_PREFIX PREFIX "tbd"
72 #define BLOCK_HEIGHT_PREFIX PREFIX "tbh"
73 #define SPAN_WIDTH_PREFIX PREFIX "w"
74 #define SPAN_LEFT_NUMERIC_WIDTH_PREFIX PREFIX "lnw"
75 #define SPAN_RIGHT_NUMERIC_WIDTH_PREFIX PREFIX "rnw"
76 #define SPAN_ALPHABETIC_WIDTH_PREFIX PREFIX "aw"
77 #define COLUMN_SEPARATION_PREFIX PREFIX "cs"
78 #define ROW_START_PREFIX PREFIX "rs"
79 #define COLUMN_START_PREFIX PREFIX "cl"
80 #define COLUMN_END_PREFIX PREFIX "ce"
81 #define COLUMN_DIVIDE_PREFIX PREFIX "cd"
82 #define ROW_TOP_PREFIX PREFIX "rt"
84 string block_width_reg(int r, int c);
85 string block_diversion_name(int r, int c);
86 string block_height_reg(int r, int c);
87 string span_width_reg(int start_col, int end_col);
88 string span_left_numeric_width_reg(int start_col, int end_col);
89 string span_right_numeric_width_reg(int start_col, int end_col);
90 string span_alphabetic_width_reg(int start_col, int end_col);
91 string column_separation_reg(int col);
92 string row_start_reg(int r);
93 string column_start_reg(int c);
94 string column_end_reg(int c);
95 string column_divide_reg(int c);
96 string row_top_reg(int r);
98 void set_inline_modifier(const entry_modifier *);
99 void restore_inline_modifier(const entry_modifier *m);
100 void set_modifier(const entry_modifier *);
101 int find_decimal_point(const char *s, char decimal_point_char,
102 const char *delim);
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 entry_modifier *mod;
154 public:
155 void set_location();
156 table_entry(const entry_modifier *);
157 virtual ~table_entry();
158 virtual int divert(int ncols, const string *mw, int *sep);
159 virtual void do_width();
160 virtual void do_depth();
161 virtual void print() = 0;
162 virtual void position_vertically() = 0;
163 virtual single_line_entry *to_single_line_entry();
164 virtual double_line_entry *to_double_line_entry();
165 virtual simple_entry *to_simple_entry();
166 virtual int line_type();
167 virtual void note_double_vrule_on_right(int);
168 virtual void note_double_vrule_on_left(int);
171 class simple_entry : public table_entry {
172 public:
173 simple_entry(const entry_modifier *);
174 void print();
175 void position_vertically();
176 simple_entry *to_simple_entry();
177 virtual void add_tab();
178 virtual void simple_print(int);
181 class empty_entry : public simple_entry {
182 public:
183 empty_entry(const entry_modifier *);
184 int line_type();
187 class text_entry : public simple_entry {
188 protected:
189 char *contents;
190 void print_contents();
191 public:
192 text_entry(char *, const entry_modifier *);
193 ~text_entry();
196 void text_entry::print_contents()
198 set_inline_modifier(mod);
199 prints(contents);
200 restore_inline_modifier(mod);
203 class repeated_char_entry : public text_entry {
204 public:
205 repeated_char_entry(char *s, const entry_modifier *m);
206 void simple_print(int);
209 class simple_text_entry : public text_entry {
210 public:
211 simple_text_entry(char *s, const entry_modifier *m);
212 void do_width();
215 class left_text_entry : public simple_text_entry {
216 public:
217 left_text_entry(char *s, const entry_modifier *m);
218 void simple_print(int);
219 void add_tab();
222 class right_text_entry : public simple_text_entry {
223 public:
224 right_text_entry(char *s, const entry_modifier *m);
225 void simple_print(int);
226 void add_tab();
229 class center_text_entry : public simple_text_entry {
230 public:
231 center_text_entry(char *s, const entry_modifier *m);
232 void simple_print(int);
233 void add_tab();
236 class numeric_text_entry : public text_entry {
237 int dot_pos;
238 public:
239 numeric_text_entry(char *s, const entry_modifier *m, int pos);
240 void do_width();
241 void simple_print(int);
244 class alphabetic_text_entry : public text_entry {
245 public:
246 alphabetic_text_entry(char *s, const entry_modifier *m);
247 void do_width();
248 void simple_print(int);
249 void add_tab();
252 class line_entry : public simple_entry {
253 protected:
254 char double_vrule_on_right;
255 char double_vrule_on_left;
256 public:
257 line_entry(const entry_modifier *);
258 void note_double_vrule_on_right(int);
259 void note_double_vrule_on_left(int);
260 void simple_print(int) = 0;
263 class single_line_entry : public line_entry {
264 public:
265 single_line_entry(const entry_modifier *m);
266 void simple_print(int);
267 single_line_entry *to_single_line_entry();
268 int line_type();
271 class double_line_entry : public line_entry {
272 public:
273 double_line_entry(const entry_modifier *m);
274 void simple_print(int);
275 double_line_entry *to_double_line_entry();
276 int line_type();
279 class short_line_entry : public simple_entry {
280 public:
281 short_line_entry(const entry_modifier *m);
282 void simple_print(int);
283 int line_type();
286 class short_double_line_entry : public simple_entry {
287 public:
288 short_double_line_entry(const entry_modifier *m);
289 void simple_print(int);
290 int line_type();
293 class block_entry : public table_entry {
294 char *contents;
295 protected:
296 void do_divert(int alphabetic, int ncols, const string *mw, int *sep);
297 public:
298 block_entry(char *s, const entry_modifier *m);
299 ~block_entry();
300 int divert(int ncols, const string *mw, int *sep);
301 void do_width();
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(char *s, const entry_modifier *m);
310 void print();
313 class right_block_entry : public block_entry {
314 public:
315 right_block_entry(char *s, const entry_modifier *m);
316 void print();
319 class center_block_entry : public block_entry {
320 public:
321 center_block_entry(char *s, const entry_modifier *m);
322 void print();
325 class alphabetic_block_entry : public block_entry {
326 public:
327 alphabetic_block_entry(char *s, const entry_modifier *m);
328 void print();
329 int divert(int ncols, const string *mw, int *sep);
332 table_entry::table_entry(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), mod(m)
338 table_entry::~table_entry()
342 int table_entry::divert(int, const string *, 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 entry_modifier *m) : table_entry(m)
392 void simple_entry::add_tab()
394 // do nothing
397 void simple_entry::simple_print(int)
399 // do nothing
402 void simple_entry::position_vertically()
404 if (start_row != end_row)
405 switch (mod->vertical_alignment) {
406 case entry_modifier::TOP:
407 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
408 break;
409 case entry_modifier::CENTER:
410 // Peform the motion in two stages so that the center is rounded
411 // vertically upwards even if net vertical motion is upwards.
412 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
413 printfs(".sp \\n[" BOTTOM_REG "]u-\\n[%1]u-1v/2u\n",
414 row_start_reg(start_row));
415 break;
416 case entry_modifier::BOTTOM:
417 printfs(".sp |\\n[%1]u+\\n[" BOTTOM_REG "]u-\\n[%1]u-1v\n",
418 row_start_reg(start_row));
419 break;
420 default:
421 assert(0);
425 void simple_entry::print()
427 prints(".ta");
428 add_tab();
429 prints('\n');
430 set_location();
431 prints("\\&");
432 simple_print(0);
433 prints('\n');
436 simple_entry *simple_entry::to_simple_entry()
438 return this;
441 empty_entry::empty_entry(const entry_modifier *m)
442 : simple_entry(m)
446 int empty_entry::line_type()
448 return 0;
451 text_entry::text_entry(char *s, const entry_modifier *m)
452 : simple_entry(m), contents(s)
456 text_entry::~text_entry()
458 a_delete contents;
461 repeated_char_entry::repeated_char_entry(char *s, const entry_modifier *m)
462 : text_entry(s, m)
466 void repeated_char_entry::simple_print(int)
468 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
469 set_inline_modifier(mod);
470 printfs("\\l" DELIMITER_CHAR "\\n[%1]u\\&",
471 span_width_reg(start_col, end_col));
472 prints(contents);
473 prints(DELIMITER_CHAR);
474 restore_inline_modifier(mod);
477 simple_text_entry::simple_text_entry(char *s, const entry_modifier *m)
478 : text_entry(s, m)
482 void simple_text_entry::do_width()
484 set_location();
485 printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR,
486 span_width_reg(start_col, end_col));
487 print_contents();
488 prints(DELIMITER_CHAR "\n");
491 left_text_entry::left_text_entry(char *s, const entry_modifier *m)
492 : simple_text_entry(s, m)
496 void left_text_entry::simple_print(int)
498 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
499 print_contents();
502 // The only point of this is to make `\a' ``work'' as in Unix tbl. Grrr.
504 void left_text_entry::add_tab()
506 printfs(" \\n[%1]u", column_end_reg(end_col));
509 right_text_entry::right_text_entry(char *s, const entry_modifier *m)
510 : simple_text_entry(s, m)
514 void right_text_entry::simple_print(int)
516 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
517 prints("\002\003");
518 print_contents();
519 prints("\002");
522 void right_text_entry::add_tab()
524 printfs(" \\n[%1]u", column_end_reg(end_col));
527 center_text_entry::center_text_entry(char *s, const entry_modifier *m)
528 : simple_text_entry(s, m)
532 void center_text_entry::simple_print(int)
534 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
535 prints("\002\003");
536 print_contents();
537 prints("\003\002");
540 void center_text_entry::add_tab()
542 printfs(" \\n[%1]u", column_end_reg(end_col));
545 numeric_text_entry::numeric_text_entry(char *s, const entry_modifier *m, int pos)
546 : text_entry(s, m), dot_pos(pos)
550 void numeric_text_entry::do_width()
552 if (dot_pos != 0) {
553 set_location();
554 printfs(".nr %1 0\\w" DELIMITER_CHAR,
555 block_width_reg(start_row, start_col));
556 set_inline_modifier(mod);
557 for (int i = 0; i < dot_pos; i++)
558 prints(contents[i]);
559 restore_inline_modifier(mod);
560 prints(DELIMITER_CHAR "\n");
561 printfs(".nr %1 \\n[%1]>?\\n[%2]\n",
562 span_left_numeric_width_reg(start_col, end_col),
563 block_width_reg(start_row, start_col));
565 else
566 printfs(".nr %1 0\n", block_width_reg(start_row, start_col));
567 if (contents[dot_pos] != '\0') {
568 set_location();
569 printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR,
570 span_right_numeric_width_reg(start_col, end_col));
571 set_inline_modifier(mod);
572 prints(contents + dot_pos);
573 restore_inline_modifier(mod);
574 prints(DELIMITER_CHAR "\n");
578 void numeric_text_entry::simple_print(int)
580 printfs("\\h'|(\\n[%1]u-\\n[%2]u-\\n[%3]u/2u+\\n[%2]u+\\n[%4]u-\\n[%5]u)'",
581 span_width_reg(start_col, end_col),
582 span_left_numeric_width_reg(start_col, end_col),
583 span_right_numeric_width_reg(start_col, end_col),
584 column_start_reg(start_col),
585 block_width_reg(start_row, start_col));
586 print_contents();
589 alphabetic_text_entry::alphabetic_text_entry(char *s, const entry_modifier *m)
590 : text_entry(s, m)
594 void alphabetic_text_entry::do_width()
596 set_location();
597 printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR,
598 span_alphabetic_width_reg(start_col, end_col));
599 print_contents();
600 prints(DELIMITER_CHAR "\n");
603 void alphabetic_text_entry::simple_print(int)
605 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
606 printfs("\\h'\\n[%1]u-\\n[%2]u/2u'",
607 span_width_reg(start_col, end_col),
608 span_alphabetic_width_reg(start_col, end_col));
609 print_contents();
612 // The only point of this is to make `\a' ``work'' as in Unix tbl. Grrr.
614 void alphabetic_text_entry::add_tab()
616 printfs(" \\n[%1]u", column_end_reg(end_col));
619 block_entry::block_entry(char *s, const entry_modifier *m)
620 : table_entry(m), contents(s)
624 block_entry::~block_entry()
626 a_delete contents;
629 void block_entry::position_vertically()
631 if (start_row != end_row)
632 switch(mod->vertical_alignment) {
633 case entry_modifier::TOP:
634 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
635 break;
636 case entry_modifier::CENTER:
637 // Peform the motion in two stages so that the center is rounded
638 // vertically upwards even if net vertical motion is upwards.
639 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
640 printfs(".sp \\n[" BOTTOM_REG "]u-\\n[%1]u-\\n[%2]u/2u\n",
641 row_start_reg(start_row),
642 block_height_reg(start_row, start_col));
643 break;
644 case entry_modifier::BOTTOM:
645 printfs(".sp |\\n[%1]u+\\n[" BOTTOM_REG "]u-\\n[%1]u-\\n[%2]u\n",
646 row_start_reg(start_row),
647 block_height_reg(start_row, start_col));
648 break;
649 default:
650 assert(0);
652 if (mod->stagger)
653 prints(".sp -.5v\n");
656 int block_entry::divert(int ncols, const string *mw, int *sep)
658 do_divert(0, ncols, mw, sep);
659 return 1;
662 void block_entry::do_divert(int alphabetic, int ncols, const string *mw,
663 int *sep)
665 printfs(".di %1\n", block_diversion_name(start_row, start_col));
666 prints(".if \\n[" SAVED_FILL_REG "] .fi\n"
667 ".in 0\n");
668 prints(".ll ");
669 int i;
670 for (i = start_col; i <= end_col; i++)
671 if (mw[i].empty())
672 break;
673 if (i > end_col) {
674 // Every column spanned by this entry has a minimum width.
675 for (int j = start_col; j <= end_col; j++) {
676 if (j > start_col) {
677 if (sep)
678 printfs("+%1n", as_string(sep[j - 1]));
679 prints('+');
681 printfs("(n;%1)", mw[j]);
683 printfs(">?\\n[%1]u", span_width_reg(start_col, end_col));
685 else
686 printfs("(u;\\n[%1]>?(\\n[.l]*%2/%3))",
687 span_width_reg(start_col, end_col),
688 as_string(end_col - start_col + 1),
689 as_string(ncols + 1));
690 if (alphabetic)
691 prints("-2n");
692 prints("\n");
693 prints(".cp \\n(" COMPATIBLE_REG "\n");
694 set_modifier(mod);
695 set_location();
696 prints(contents);
697 prints(".br\n.di\n.cp 0\n");
698 if (!mod->zero_width) {
699 if (alphabetic) {
700 printfs(".nr %1 \\n[%1]>?(\\n[dl]+2n)\n",
701 span_width_reg(start_col, end_col));
702 printfs(".nr %1 \\n[%1]>?\\n[dl]\n",
703 span_alphabetic_width_reg(start_col, end_col));
705 else
706 printfs(".nr %1 \\n[%1]>?\\n[dl]\n", span_width_reg(start_col, end_col));
708 printfs(".nr %1 \\n[dn]\n", block_height_reg(start_row, start_col));
709 printfs(".nr %1 \\n[dl]\n", block_width_reg(start_row, start_col));
710 prints("." RESET_MACRO_NAME "\n"
711 ".in \\n[" SAVED_INDENT_REG "]u\n"
712 ".nf\n");
713 // the block might have contained .lf commands
714 location_force_filename = 1;
717 void block_entry::do_width()
719 // do nothing; the action happens in divert
722 void block_entry::do_depth()
724 printfs(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?(\\n[%1]+\\n[%2])\n",
725 row_start_reg(start_row),
726 block_height_reg(start_row, start_col));
729 left_block_entry::left_block_entry(char *s, const entry_modifier *m)
730 : block_entry(s, m)
734 void left_block_entry::print()
736 printfs(".in +\\n[%1]u\n", column_start_reg(start_col));
737 printfs(".%1\n", block_diversion_name(start_row, start_col));
738 prints(".in\n");
741 right_block_entry::right_block_entry(char *s, const entry_modifier *m)
742 : block_entry(s, m)
746 void right_block_entry::print()
748 printfs(".in +\\n[%1]u+\\n[%2]u-\\n[%3]u\n",
749 column_start_reg(start_col),
750 span_width_reg(start_col, end_col),
751 block_width_reg(start_row, start_col));
752 printfs(".%1\n", block_diversion_name(start_row, start_col));
753 prints(".in\n");
756 center_block_entry::center_block_entry(char *s, const entry_modifier *m)
757 : block_entry(s, m)
761 void center_block_entry::print()
763 printfs(".in +\\n[%1]u+(\\n[%2]u-\\n[%3]u/2u)\n",
764 column_start_reg(start_col),
765 span_width_reg(start_col, end_col),
766 block_width_reg(start_row, start_col));
767 printfs(".%1\n", block_diversion_name(start_row, start_col));
768 prints(".in\n");
771 alphabetic_block_entry::alphabetic_block_entry(char *s,
772 const entry_modifier *m)
773 : block_entry(s, m)
777 int alphabetic_block_entry::divert(int ncols, const string *mw, int *sep)
779 do_divert(1, ncols, mw, sep);
780 return 1;
783 void alphabetic_block_entry::print()
785 printfs(".in +\\n[%1]u+(\\n[%2]u-\\n[%3]u/2u)\n",
786 column_start_reg(start_col),
787 span_width_reg(start_col, end_col),
788 span_alphabetic_width_reg(start_col, end_col));
789 printfs(".%1\n", block_diversion_name(start_row, start_col));
790 prints(".in\n");
793 line_entry::line_entry(const entry_modifier *m)
794 : simple_entry(m), double_vrule_on_right(0), double_vrule_on_left(0)
798 void line_entry::note_double_vrule_on_right(int is_corner)
800 double_vrule_on_right = is_corner ? 1 : 2;
803 void line_entry::note_double_vrule_on_left(int is_corner)
805 double_vrule_on_left = is_corner ? 1 : 2;
808 single_line_entry::single_line_entry(const entry_modifier *m)
809 : line_entry(m)
813 int single_line_entry::line_type()
815 return 1;
818 void single_line_entry::simple_print(int dont_move)
820 printfs("\\h'|\\n[%1]u",
821 column_divide_reg(start_col));
822 if (double_vrule_on_left) {
823 prints(double_vrule_on_left == 1 ? "-" : "+");
824 prints(HALF_DOUBLE_LINE_SEP);
826 prints("'");
827 if (!dont_move)
828 prints("\\v'-" BAR_HEIGHT "'");
829 printfs("\\s[\\n[" LINESIZE_REG "]]" "\\D'l |\\n[%1]u",
830 column_divide_reg(end_col+1));
831 if (double_vrule_on_right) {
832 prints(double_vrule_on_left == 1 ? "+" : "-");
833 prints(HALF_DOUBLE_LINE_SEP);
835 prints("0'\\s0");
836 if (!dont_move)
837 prints("\\v'" BAR_HEIGHT "'");
840 single_line_entry *single_line_entry::to_single_line_entry()
842 return this;
845 double_line_entry::double_line_entry(const entry_modifier *m)
846 : line_entry(m)
850 int double_line_entry::line_type()
852 return 2;
855 void double_line_entry::simple_print(int dont_move)
857 if (!dont_move)
858 prints("\\v'-" BAR_HEIGHT "'");
859 printfs("\\h'|\\n[%1]u",
860 column_divide_reg(start_col));
861 if (double_vrule_on_left) {
862 prints(double_vrule_on_left == 1 ? "-" : "+");
863 prints(HALF_DOUBLE_LINE_SEP);
865 prints("'");
866 printfs("\\v'-" HALF_DOUBLE_LINE_SEP "'"
867 "\\s[\\n[" LINESIZE_REG "]]"
868 "\\D'l |\\n[%1]u",
869 column_divide_reg(end_col+1));
870 if (double_vrule_on_right)
871 prints("-" HALF_DOUBLE_LINE_SEP);
872 prints(" 0'");
873 printfs("\\v'" DOUBLE_LINE_SEP "'"
874 "\\D'l |\\n[%1]u",
875 column_divide_reg(start_col));
876 if (double_vrule_on_right) {
877 prints(double_vrule_on_left == 1 ? "+" : "-");
878 prints(HALF_DOUBLE_LINE_SEP);
880 prints(" 0'");
881 prints("\\s0"
882 "\\v'-" HALF_DOUBLE_LINE_SEP "'");
883 if (!dont_move)
884 prints("\\v'" BAR_HEIGHT "'");
887 double_line_entry *double_line_entry::to_double_line_entry()
889 return this;
892 short_line_entry::short_line_entry(const entry_modifier *m)
893 : simple_entry(m)
897 int short_line_entry::line_type()
899 return 1;
902 void short_line_entry::simple_print(int dont_move)
904 if (mod->stagger)
905 prints("\\v'-.5v'");
906 if (!dont_move)
907 prints("\\v'-" BAR_HEIGHT "'");
908 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
909 printfs("\\s[\\n[" LINESIZE_REG "]]"
910 "\\D'l \\n[%1]u 0'"
911 "\\s0",
912 span_width_reg(start_col, end_col));
913 if (!dont_move)
914 prints("\\v'" BAR_HEIGHT "'");
915 if (mod->stagger)
916 prints("\\v'.5v'");
919 short_double_line_entry::short_double_line_entry(const entry_modifier *m)
920 : simple_entry(m)
924 int short_double_line_entry::line_type()
926 return 2;
929 void short_double_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[%2]u'"
936 "\\v'-" HALF_DOUBLE_LINE_SEP "'"
937 "\\s[\\n[" LINESIZE_REG "]]"
938 "\\D'l \\n[%1]u 0'"
939 "\\v'" DOUBLE_LINE_SEP "'"
940 "\\D'l |\\n[%2]u 0'"
941 "\\s0"
942 "\\v'-" HALF_DOUBLE_LINE_SEP "'",
943 span_width_reg(start_col, end_col),
944 column_start_reg(start_col));
945 if (!dont_move)
946 prints("\\v'" BAR_HEIGHT "'");
947 if (mod->stagger)
948 prints("\\v'.5v'");
951 void set_modifier(const entry_modifier *m)
953 if (!m->font.empty())
954 printfs(".ft %1\n", m->font);
955 if (m->point_size.val != 0) {
956 prints(".ps ");
957 if (m->point_size.inc > 0)
958 prints('+');
959 else if (m->point_size.inc < 0)
960 prints('-');
961 printfs("%1\n", as_string(m->point_size.val));
963 if (m->vertical_spacing.val != 0) {
964 prints(".vs ");
965 if (m->vertical_spacing.inc > 0)
966 prints('+');
967 else if (m->vertical_spacing.inc < 0)
968 prints('-');
969 printfs("%1\n", as_string(m->vertical_spacing.val));
971 if (!m->macro.empty())
972 printfs(".%1\n", m->macro);
975 void set_inline_modifier(const entry_modifier *m)
977 if (!m->font.empty())
978 printfs("\\f[%1]", m->font);
979 if (m->point_size.val != 0) {
980 prints("\\s[");
981 if (m->point_size.inc > 0)
982 prints('+');
983 else if (m->point_size.inc < 0)
984 prints('-');
985 printfs("%1]", as_string(m->point_size.val));
987 if (m->stagger)
988 prints("\\v'-.5v'");
991 void restore_inline_modifier(const entry_modifier *m)
993 if (!m->font.empty())
994 prints("\\f[\\n[" SAVED_FONT_REG "]]");
995 if (m->point_size.val != 0)
996 prints("\\s[\\n[" SAVED_SIZE_REG "]]");
997 if (m->stagger)
998 prints("\\v'.5v'");
1001 struct stuff {
1002 stuff *next;
1003 int row; // occurs before row `row'
1004 char printed; // has it been printed?
1006 stuff(int);
1007 virtual void print(table *) = 0;
1008 virtual ~stuff();
1009 virtual int is_single_line() { return 0; };
1010 virtual int is_double_line() { return 0; };
1013 stuff::stuff(int r) : next(0), row(r), printed(0)
1017 stuff::~stuff()
1021 struct text_stuff : public stuff {
1022 string contents;
1023 const char *filename;
1024 int lineno;
1026 text_stuff(const string &, int r, const char *fn, int ln);
1027 ~text_stuff();
1028 void print(table *);
1031 text_stuff::text_stuff(const string &s, int r, const char *fn, int ln)
1032 : stuff(r), contents(s), filename(fn), lineno(ln)
1036 text_stuff::~text_stuff()
1040 void text_stuff::print(table *)
1042 printed = 1;
1043 prints(".cp \\n(" COMPATIBLE_REG "\n");
1044 set_troff_location(filename, lineno);
1045 prints(contents);
1046 prints(".cp 0\n");
1047 location_force_filename = 1; // it might have been a .lf command
1050 struct single_hline_stuff : public stuff {
1051 single_hline_stuff(int r);
1052 void print(table *);
1053 int is_single_line();
1056 single_hline_stuff::single_hline_stuff(int r) : stuff(r)
1060 void single_hline_stuff::print(table *tbl)
1062 printed = 1;
1063 tbl->print_single_hline(row);
1066 int single_hline_stuff::is_single_line()
1068 return 1;
1071 struct double_hline_stuff : stuff {
1072 double_hline_stuff(int r);
1073 void print(table *);
1074 int is_double_line();
1077 double_hline_stuff::double_hline_stuff(int r) : stuff(r)
1081 void double_hline_stuff::print(table *tbl)
1083 printed = 1;
1084 tbl->print_double_hline(row);
1087 int double_hline_stuff::is_double_line()
1089 return 1;
1092 struct vertical_rule {
1093 vertical_rule *next;
1094 int start_row;
1095 int end_row;
1096 int col;
1097 char is_double;
1098 string top_adjust;
1099 string bot_adjust;
1101 vertical_rule(int sr, int er, int c, int dbl, vertical_rule *);
1102 ~vertical_rule();
1103 void contribute_to_bottom_macro(table *);
1104 void print();
1107 vertical_rule::vertical_rule(int sr, int er, int c, int dbl, vertical_rule *p)
1108 : next(p), start_row(sr), end_row(er), col(c), is_double(dbl)
1112 vertical_rule::~vertical_rule()
1116 void vertical_rule::contribute_to_bottom_macro(table *tbl)
1118 printfs(".if \\n[" CURRENT_ROW_REG "]>=%1",
1119 as_string(start_row));
1120 if (end_row != tbl->get_nrows() - 1)
1121 printfs("&(\\n[" CURRENT_ROW_REG "]<%1)",
1122 as_string(end_row));
1123 prints(" \\{");
1124 printfs(".if %1<=\\n[" LAST_PASSED_ROW_REG "] .nr %2 \\n[#T]\n",
1125 as_string(start_row),
1126 row_top_reg(start_row));
1127 const char *offset_table[3];
1128 if (is_double) {
1129 offset_table[0] = "-" HALF_DOUBLE_LINE_SEP;
1130 offset_table[1] = "+" HALF_DOUBLE_LINE_SEP;
1131 offset_table[2] = 0;
1133 else {
1134 offset_table[0] = "";
1135 offset_table[1] = 0;
1137 for (const char **offsetp = offset_table; *offsetp; offsetp++) {
1138 prints(".sp -1\n"
1139 "\\v'" BODY_DEPTH);
1140 if (!bot_adjust.empty())
1141 printfs("+%1", bot_adjust);
1142 prints("'");
1143 printfs("\\h'\\n[%1]u%3'\\s[\\n[" LINESIZE_REG "]]\\D'l 0 |\\n[%2]u-1v",
1144 column_divide_reg(col),
1145 row_top_reg(start_row),
1146 *offsetp);
1147 if (!bot_adjust.empty())
1148 printfs("-(%1)", bot_adjust);
1149 // don't perform the top adjustment if the top is actually #T
1150 if (!top_adjust.empty())
1151 printfs("+((%1)*(%2>\\n[" LAST_PASSED_ROW_REG "]))",
1152 top_adjust,
1153 as_string(start_row));
1154 prints("'\\s0\n");
1156 prints(".\\}\n");
1159 void vertical_rule::print()
1161 printfs("\\*[" TRANSPARENT_STRING_NAME "]"
1162 ".if %1<=\\*[" QUOTE_STRING_NAME "]\\n[" LAST_PASSED_ROW_REG "] "
1163 ".nr %2 \\*[" QUOTE_STRING_NAME "]\\n[#T]\n",
1164 as_string(start_row),
1165 row_top_reg(start_row));
1166 const char *offset_table[3];
1167 if (is_double) {
1168 offset_table[0] = "-" HALF_DOUBLE_LINE_SEP;
1169 offset_table[1] = "+" HALF_DOUBLE_LINE_SEP;
1170 offset_table[2] = 0;
1172 else {
1173 offset_table[0] = "";
1174 offset_table[1] = 0;
1176 for (const char **offsetp = offset_table; *offsetp; offsetp++) {
1177 prints("\\*[" TRANSPARENT_STRING_NAME "].sp -1\n"
1178 "\\*[" TRANSPARENT_STRING_NAME "]\\v'" BODY_DEPTH);
1179 if (!bot_adjust.empty())
1180 printfs("+%1", bot_adjust);
1181 prints("'");
1182 printfs("\\h'\\n[%1]u%3'"
1183 "\\s[\\n[" LINESIZE_REG "]]"
1184 "\\D'l 0 |\\*[" QUOTE_STRING_NAME "]\\n[%2]u-1v",
1185 column_divide_reg(col),
1186 row_top_reg(start_row),
1187 *offsetp);
1188 if (!bot_adjust.empty())
1189 printfs("-(%1)", bot_adjust);
1190 // don't perform the top adjustment if the top is actually #T
1191 if (!top_adjust.empty())
1192 printfs("+((%1)*(%2>\\*[" QUOTE_STRING_NAME "]\\n["
1193 LAST_PASSED_ROW_REG "]))",
1194 top_adjust,
1195 as_string(start_row));
1196 prints("'"
1197 "\\s0\n");
1201 table::table(int nc, unsigned f, int ls, char dpc)
1202 : flags(f), nrows(0), ncolumns(nc), linesize(ls), decimal_point_char(dpc),
1203 vrule_list(0), stuff_list(0), span_list(0),
1204 entry_list(0), entry_list_tailp(&entry_list), entry(0),
1205 vline(0), row_is_all_lines(0), left_separation(0), right_separation(0),
1206 allocated_rows(0)
1208 minimum_width = new string[ncolumns];
1209 column_separation = ncolumns > 1 ? new int[ncolumns - 1] : 0;
1210 equal = new char[ncolumns];
1211 int i;
1212 for (i = 0; i < ncolumns; i++)
1213 equal[i] = 0;
1214 for (i = 0; i < ncolumns-1; i++)
1215 column_separation[i] = DEFAULT_COLUMN_SEPARATION;
1216 delim[0] = delim[1] = '\0';
1219 table::~table()
1221 for (int i = 0; i < nrows; i++) {
1222 a_delete entry[i];
1223 a_delete vline[i];
1225 a_delete entry;
1226 a_delete vline;
1227 while (entry_list) {
1228 table_entry *tem = entry_list;
1229 entry_list = entry_list->next;
1230 delete tem;
1232 ad_delete(ncolumns) minimum_width;
1233 a_delete column_separation;
1234 a_delete equal;
1235 while (stuff_list) {
1236 stuff *tem = stuff_list;
1237 stuff_list = stuff_list->next;
1238 delete tem;
1240 while (vrule_list) {
1241 vertical_rule *tem = vrule_list;
1242 vrule_list = vrule_list->next;
1243 delete tem;
1245 a_delete row_is_all_lines;
1246 while (span_list) {
1247 horizontal_span *tem = span_list;
1248 span_list = span_list->next;
1249 delete tem;
1253 void table::set_delim(char c1, char c2)
1255 delim[0] = c1;
1256 delim[1] = c2;
1259 void table::set_minimum_width(int c, const string &w)
1261 assert(c >= 0 && c < ncolumns);
1262 minimum_width[c] = w;
1265 void table::set_column_separation(int c, int n)
1267 assert(c >= 0 && c < ncolumns - 1);
1268 column_separation[c] = n;
1271 void table::set_equal_column(int c)
1273 assert(c >= 0 && c < ncolumns);
1274 equal[c] = 1;
1277 void table::add_stuff(stuff *p)
1279 stuff **pp;
1280 for (pp = &stuff_list; *pp; pp = &(*pp)->next)
1282 *pp = p;
1285 void table::add_text_line(int r, const string &s, const char *filename, int lineno)
1287 add_stuff(new text_stuff(s, r, filename, lineno));
1290 void table::add_single_hline(int r)
1292 add_stuff(new single_hline_stuff(r));
1295 void table::add_double_hline(int r)
1297 add_stuff(new double_hline_stuff(r));
1300 void table::allocate(int r)
1302 if (r >= nrows) {
1303 typedef table_entry **PPtable_entry; // work around g++ 1.36.1 bug
1304 if (r >= allocated_rows) {
1305 if (allocated_rows == 0) {
1306 allocated_rows = 16;
1307 if (allocated_rows <= r)
1308 allocated_rows = r + 1;
1309 entry = new PPtable_entry[allocated_rows];
1310 vline = new char*[allocated_rows];
1312 else {
1313 table_entry ***old_entry = entry;
1314 int old_allocated_rows = allocated_rows;
1315 allocated_rows *= 2;
1316 if (allocated_rows <= r)
1317 allocated_rows = r + 1;
1318 entry = new PPtable_entry[allocated_rows];
1319 memcpy(entry, old_entry, sizeof(table_entry**)*old_allocated_rows);
1320 a_delete old_entry;
1321 char **old_vline = vline;
1322 vline = new char*[allocated_rows];
1323 memcpy(vline, old_vline, sizeof(char*)*old_allocated_rows);
1324 a_delete old_vline;
1327 assert(allocated_rows > r);
1328 while (nrows <= r) {
1329 entry[nrows] = new table_entry*[ncolumns];
1330 int i;
1331 for (i = 0; i < ncolumns; i++)
1332 entry[nrows][i] = 0;
1333 vline[nrows] = new char[ncolumns+1];
1334 for (i = 0; i < ncolumns+1; i++)
1335 vline[nrows][i] = 0;
1336 nrows++;
1341 void table::do_hspan(int r, int c)
1343 assert(r >= 0 && c >= 0 && r < nrows && c < ncolumns);
1344 if (c == 0) {
1345 error("first column cannot be horizontally spanned");
1346 return;
1348 table_entry *e = entry[r][c];
1349 if (e) {
1350 assert(e->start_row <= r && r <= e->end_row
1351 && e->start_col <= c && c <= e->end_col
1352 && e->end_row - e->start_row > 0
1353 && e->end_col - e->start_col > 0);
1354 return;
1356 e = entry[r][c-1];
1357 // e can be 0 if we had an empty entry or an error
1358 if (e == 0)
1359 return;
1360 if (e->start_row != r) {
1363 ^ s */
1364 error("impossible horizontal span at row %1, column %2", r + 1, c + 1);
1366 else {
1367 e->end_col = c;
1368 entry[r][c] = e;
1372 void table::do_vspan(int r, int c)
1374 assert(r >= 0 && c >= 0 && r < nrows && c < ncolumns);
1375 if (r == 0) {
1376 error("first row cannot be vertically spanned");
1377 return;
1379 table_entry *e = entry[r][c];
1380 if (e) {
1381 assert(e->start_row <= r && r <= e->end_row
1382 && e->start_col <= c && c <= e->end_col
1383 && e->end_row - e->start_row > 0
1384 && e->end_col - e->start_col > 0);
1385 return;
1387 e = entry[r-1][c];
1388 // e can be 0 if we had an empty entry or an error
1389 if (e == 0)
1390 return;
1391 if (e->start_col != c) {
1392 /* l s
1393 l ^ */
1394 error("impossible vertical span at row %1, column %2", r + 1, c + 1);
1396 else {
1397 for (int i = c; i <= e->end_col; i++) {
1398 assert(entry[r][i] == 0);
1399 entry[r][i] = e;
1401 e->end_row = r;
1405 int find_decimal_point(const char *s, char decimal_point_char,
1406 const char *delim)
1408 if (s == 0 || *s == '\0')
1409 return -1;
1410 const char *p;
1411 int in_delim = 0; // is p within eqn delimiters?
1412 // tbl recognises \& even within eqn delimiters; I don't
1413 for (p = s; *p; p++)
1414 if (in_delim) {
1415 if (*p == delim[1])
1416 in_delim = 0;
1418 else if (*p == delim[0])
1419 in_delim = 1;
1420 else if (p[0] == '\\' && p[1] == '&')
1421 return p - s;
1422 int possible_pos = -1;
1423 in_delim = 0;
1424 for (p = s; *p; p++)
1425 if (in_delim) {
1426 if (*p == delim[1])
1427 in_delim = 0;
1429 else if (*p == delim[0])
1430 in_delim = 1;
1431 else if (p[0] == decimal_point_char && csdigit(p[1]))
1432 possible_pos = p - s;
1433 if (possible_pos >= 0)
1434 return possible_pos;
1435 in_delim = 0;
1436 for (p = s; *p; p++)
1437 if (in_delim) {
1438 if (*p == delim[1])
1439 in_delim = 0;
1441 else if (*p == delim[0])
1442 in_delim = 1;
1443 else if (csdigit(*p))
1444 possible_pos = p + 1 - s;
1445 return possible_pos;
1448 void table::add_entry(int r, int c, const string &str, const entry_format *f,
1449 const char *fn, int ln)
1451 allocate(r);
1452 table_entry *e = 0;
1453 if (str == "\\_") {
1454 e = new short_line_entry(f);
1456 else if (str == "\\=") {
1457 e = new short_double_line_entry(f);
1459 else if (str == "_") {
1460 single_line_entry *lefte;
1461 if (c > 0 && entry[r][c-1] != 0 &&
1462 (lefte = entry[r][c-1]->to_single_line_entry()) != 0
1463 && lefte->start_row == r
1464 && lefte->mod->stagger == f->stagger) {
1465 lefte->end_col = c;
1466 entry[r][c] = lefte;
1468 else
1469 e = new single_line_entry(f);
1471 else if (str == "=") {
1472 double_line_entry *lefte;
1473 if (c > 0 && entry[r][c-1] != 0 &&
1474 (lefte = entry[r][c-1]->to_double_line_entry()) != 0
1475 && lefte->start_row == r
1476 && lefte->mod->stagger == f->stagger) {
1477 lefte->end_col = c;
1478 entry[r][c] = lefte;
1480 else
1481 e = new double_line_entry(f);
1483 else if (str == "\\^") {
1484 do_vspan(r, c);
1486 else if (str.length() > 2 && str[0] == '\\' && str[1] == 'R') {
1487 if (str.search('\n') >= 0)
1488 error_with_file_and_line(fn, ln, "bad repeated character");
1489 else {
1490 char *s = str.substring(2, str.length() - 2).extract();
1491 e = new repeated_char_entry(s, f);
1494 else {
1495 int is_block = str.search('\n') >= 0;
1496 char *s;
1497 switch (f->type) {
1498 case FORMAT_SPAN:
1499 assert(str.empty());
1500 do_hspan(r, c);
1501 break;
1502 case FORMAT_LEFT:
1503 if (!str.empty()) {
1504 s = str.extract();
1505 if (is_block)
1506 e = new left_block_entry(s, f);
1507 else
1508 e = new left_text_entry(s, f);
1510 else
1511 e = new empty_entry(f);
1512 break;
1513 case FORMAT_CENTER:
1514 if (!str.empty()) {
1515 s = str.extract();
1516 if (is_block)
1517 e = new center_block_entry(s, f);
1518 else
1519 e = new center_text_entry(s, f);
1521 else
1522 e = new empty_entry(f);
1523 break;
1524 case FORMAT_RIGHT:
1525 if (!str.empty()) {
1526 s = str.extract();
1527 if (is_block)
1528 e = new right_block_entry(s, f);
1529 else
1530 e = new right_text_entry(s, f);
1532 else
1533 e = new empty_entry(f);
1534 break;
1535 case FORMAT_NUMERIC:
1536 if (!str.empty()) {
1537 s = str.extract();
1538 if (is_block) {
1539 error_with_file_and_line(fn, ln, "can't have numeric text block");
1540 e = new left_block_entry(s, f);
1542 else {
1543 int pos = find_decimal_point(s, decimal_point_char, delim);
1544 if (pos < 0)
1545 e = new center_text_entry(s, f);
1546 else
1547 e = new numeric_text_entry(s, f, pos);
1550 else
1551 e = new empty_entry(f);
1552 break;
1553 case FORMAT_ALPHABETIC:
1554 if (!str.empty()) {
1555 s = str.extract();
1556 if (is_block)
1557 e = new alphabetic_block_entry(s, f);
1558 else
1559 e = new alphabetic_text_entry(s, f);
1561 else
1562 e = new empty_entry(f);
1563 break;
1564 case FORMAT_VSPAN:
1565 do_vspan(r, c);
1566 break;
1567 case FORMAT_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 single_line_entry(f);
1572 break;
1573 case FORMAT_DOUBLE_HLINE:
1574 if (str.length() != 0)
1575 error_with_file_and_line(fn, ln,
1576 "non-empty data entry for `=' format ignored");
1577 e = new double_line_entry(f);
1578 break;
1579 default:
1580 assert(0);
1583 if (e) {
1584 table_entry *preve = entry[r][c];
1585 if (preve) {
1586 /* c s
1587 ^ l */
1588 error_with_file_and_line(fn, ln, "row %1, column %2 already spanned",
1589 r + 1, c + 1);
1590 delete e;
1592 else {
1593 e->input_lineno = ln;
1594 e->input_filename = fn;
1595 e->start_row = e->end_row = r;
1596 e->start_col = e->end_col = c;
1597 *entry_list_tailp = e;
1598 entry_list_tailp = &e->next;
1599 entry[r][c] = e;
1604 // add vertical lines for row r
1606 void table::add_vlines(int r, const char *v)
1608 allocate(r);
1609 for (int i = 0; i < ncolumns+1; i++)
1610 vline[r][i] = v[i];
1613 void table::check()
1615 table_entry *p = entry_list;
1616 int i, j;
1617 while (p) {
1618 for (i = p->start_row; i <= p->end_row; i++)
1619 for (j = p->start_col; j <= p->end_col; j++)
1620 assert(entry[i][j] == p);
1621 p = p->next;
1625 void table::print()
1627 location_force_filename = 1;
1628 check();
1629 init_output();
1630 determine_row_type();
1631 compute_widths();
1632 if (!(flags & CENTER))
1633 prints(".if \\n[" SAVED_CENTER_REG "] \\{");
1634 prints(".in +(u;\\n[.l]-\\n[.i]-\\n[TW]/2>?-\\n[.i])\n"
1635 ".nr " SAVED_INDENT_REG " \\n[.i]\n");
1636 if (!(flags & CENTER))
1637 prints(".\\}\n");
1638 build_vrule_list();
1639 define_bottom_macro();
1640 do_top();
1641 for (int i = 0; i < nrows; i++)
1642 do_row(i);
1643 do_bottom();
1646 void table::determine_row_type()
1648 row_is_all_lines = new char[nrows];
1649 for (int i = 0; i < nrows; i++) {
1650 int had_single = 0;
1651 int had_double = 0;
1652 int had_non_line = 0;
1653 for (int c = 0; c < ncolumns; c++) {
1654 table_entry *e = entry[i][c];
1655 if (e != 0) {
1656 if (e->start_row == e->end_row) {
1657 int t = e->line_type();
1658 switch (t) {
1659 case -1:
1660 had_non_line = 1;
1661 break;
1662 case 0:
1663 // empty
1664 break;
1665 case 1:
1666 had_single = 1;
1667 break;
1668 case 2:
1669 had_double = 1;
1670 break;
1671 default:
1672 assert(0);
1674 if (had_non_line)
1675 break;
1677 c = e->end_col;
1680 if (had_non_line)
1681 row_is_all_lines[i] = 0;
1682 else if (had_double)
1683 row_is_all_lines[i] = 2;
1684 else if (had_single)
1685 row_is_all_lines[i] = 1;
1686 else
1687 row_is_all_lines[i] = 0;
1691 void table::init_output()
1693 prints(".nr " COMPATIBLE_REG " \\n(.C\n"
1694 ".cp 0\n");
1695 if (linesize > 0)
1696 printfs(".nr " LINESIZE_REG " %1\n", as_string(linesize));
1697 else
1698 prints(".nr " LINESIZE_REG " \\n[.s]\n");
1699 if (!(flags & CENTER))
1700 prints(".nr " SAVED_CENTER_REG " \\n[.ce]\n");
1701 if (compatible_flag)
1702 prints(".ds " LEADER_REG " \\a\n");
1703 prints(".de " RESET_MACRO_NAME "\n"
1704 ".ft \\n[.f]\n"
1705 ".ps \\n[.s]\n"
1706 ".vs \\n[.v]u\n"
1707 ".in \\n[.i]u\n"
1708 ".ll \\n[.l]u\n"
1709 ".ls \\n[.L]\n"
1710 ".ad \\n[.j]\n"
1711 ".ie \\n[.u] .fi\n"
1712 ".el .nf\n"
1713 ".ce \\n[.ce]\n"
1714 "..\n"
1715 ".nr " SAVED_INDENT_REG " \\n[.i]\n"
1716 ".nr " SAVED_FONT_REG " \\n[.f]\n"
1717 ".nr " SAVED_SIZE_REG " \\n[.s]\n"
1718 ".nr " SAVED_FILL_REG " \\n[.u]\n"
1719 ".nr T. 0\n"
1720 ".nr " CURRENT_ROW_REG " 0-1\n"
1721 ".nr " LAST_PASSED_ROW_REG " 0-1\n"
1722 ".nr " SECTION_DIVERSION_FLAG_REG " 0\n"
1723 ".ds " TRANSPARENT_STRING_NAME "\n"
1724 ".ds " QUOTE_STRING_NAME "\n"
1725 ".nr " NEED_BOTTOM_RULE_REG " 1\n"
1726 ".nr " SUPPRESS_BOTTOM_REG " 0\n"
1727 ".eo\n"
1728 ".de " REPEATED_MARK_MACRO "\n"
1729 ".mk \\$1\n"
1730 ".if !'\\n(.z'' \\!." REPEATED_MARK_MACRO " \"\\$1\"\n"
1731 "..\n"
1732 ".de " REPEATED_VPT_MACRO "\n"
1733 ".vpt \\$1\n"
1734 ".if !'\\n(.z'' \\!." REPEATED_VPT_MACRO " \"\\$1\"\n"
1735 "..\n");
1736 if (!(flags & NOKEEP))
1737 prints(".de " KEEP_MACRO_NAME "\n"
1738 ".if '\\n[.z]'' \\{.ds " QUOTE_STRING_NAME " \\\\\n"
1739 ".ds " TRANSPARENT_STRING_NAME " \\!\n"
1740 ".di " SECTION_DIVERSION_NAME "\n"
1741 ".nr " SECTION_DIVERSION_FLAG_REG " 1\n"
1742 ".in 0\n"
1743 ".\\}\n"
1744 "..\n"
1745 ".de " RELEASE_MACRO_NAME "\n"
1746 ".if \\n[" SECTION_DIVERSION_FLAG_REG "] \\{"
1747 ".di\n"
1748 ".in \\n[" SAVED_INDENT_REG "]u\n"
1749 ".nr " SAVED_DN_REG " \\n[dn]\n"
1750 ".ds " QUOTE_STRING_NAME "\n"
1751 ".ds " TRANSPARENT_STRING_NAME "\n"
1752 ".nr " SECTION_DIVERSION_FLAG_REG " 0\n"
1753 ".if \\n[.t]<=\\n[dn] \\{"
1754 ".nr T. 1\n"
1755 ".T#\n"
1756 ".nr " SUPPRESS_BOTTOM_REG " 1\n"
1757 ".sp \\n[.t]u\n"
1758 ".nr " SUPPRESS_BOTTOM_REG " 0\n"
1759 ".mk #T\n"
1760 ".\\}\n"
1761 ".if \\n[.t]<=\\n[" SAVED_DN_REG "] "
1762 /* Since we turn off traps, it won't get into an infinite loop
1763 when we try and print it; it will just go off the bottom of the
1764 page. */
1765 ".tm warning: page \\n%: table text block will not fit on one page\n"
1766 ".nf\n"
1767 ".ls 1\n"
1768 "." SECTION_DIVERSION_NAME "\n"
1769 ".ls\n"
1770 ".rm " SECTION_DIVERSION_NAME "\n"
1771 ".\\}\n"
1772 "..\n"
1773 ".nr " TABLE_DIVERSION_FLAG_REG " 0\n"
1774 ".de " TABLE_KEEP_MACRO_NAME "\n"
1775 ".if '\\n[.z]'' \\{"
1776 ".di " TABLE_DIVERSION_NAME "\n"
1777 ".nr " TABLE_DIVERSION_FLAG_REG " 1\n"
1778 ".\\}\n"
1779 "..\n"
1780 ".de " TABLE_RELEASE_MACRO_NAME "\n"
1781 ".if \\n[" TABLE_DIVERSION_FLAG_REG "] \\{.br\n"
1782 ".di\n"
1783 ".nr " SAVED_DN_REG " \\n[dn]\n"
1784 ".ne \\n[dn]u+\\n[.V]u\n"
1785 ".ie \\n[.t]<=\\n[" SAVED_DN_REG "] "
1786 ".tm error: page \\n%: table will not fit on one page; use .TS H/.TH with a supporting macro package\n"
1787 ".el \\{"
1788 ".in 0\n"
1789 ".ls 1\n"
1790 ".nf\n"
1791 "." TABLE_DIVERSION_NAME "\n"
1792 ".\\}\n"
1793 ".rm " TABLE_DIVERSION_NAME "\n"
1794 ".\\}\n"
1795 "..\n");
1796 prints(".ec\n"
1797 ".ce 0\n"
1798 ".nf\n");
1801 string block_width_reg(int r, int c)
1803 static char name[sizeof(BLOCK_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1804 sprintf(name, BLOCK_WIDTH_PREFIX "%d,%d", r, c);
1805 return string(name);
1808 string block_diversion_name(int r, int c)
1810 static char name[sizeof(BLOCK_DIVERSION_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1811 sprintf(name, BLOCK_DIVERSION_PREFIX "%d,%d", r, c);
1812 return string(name);
1815 string block_height_reg(int r, int c)
1817 static char name[sizeof(BLOCK_HEIGHT_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1818 sprintf(name, BLOCK_HEIGHT_PREFIX "%d,%d", r, c);
1819 return string(name);
1822 string span_width_reg(int start_col, int end_col)
1824 static char name[sizeof(SPAN_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1825 sprintf(name, SPAN_WIDTH_PREFIX "%d", start_col);
1826 if (end_col != start_col)
1827 sprintf(strchr(name, '\0'), ",%d", end_col);
1828 return string(name);
1831 string span_left_numeric_width_reg(int start_col, int end_col)
1833 static char name[sizeof(SPAN_LEFT_NUMERIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1834 sprintf(name, SPAN_LEFT_NUMERIC_WIDTH_PREFIX "%d", start_col);
1835 if (end_col != start_col)
1836 sprintf(strchr(name, '\0'), ",%d", end_col);
1837 return string(name);
1840 string span_right_numeric_width_reg(int start_col, int end_col)
1842 static char name[sizeof(SPAN_RIGHT_NUMERIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1843 sprintf(name, SPAN_RIGHT_NUMERIC_WIDTH_PREFIX "%d", start_col);
1844 if (end_col != start_col)
1845 sprintf(strchr(name, '\0'), ",%d", end_col);
1846 return string(name);
1849 string span_alphabetic_width_reg(int start_col, int end_col)
1851 static char name[sizeof(SPAN_ALPHABETIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1852 sprintf(name, SPAN_ALPHABETIC_WIDTH_PREFIX "%d", start_col);
1853 if (end_col != start_col)
1854 sprintf(strchr(name, '\0'), ",%d", end_col);
1855 return string(name);
1858 string column_separation_reg(int col)
1860 static char name[sizeof(COLUMN_SEPARATION_PREFIX)+INT_DIGITS];
1861 sprintf(name, COLUMN_SEPARATION_PREFIX "%d", col);
1862 return string(name);
1865 string row_start_reg(int row)
1867 static char name[sizeof(ROW_START_PREFIX)+INT_DIGITS];
1868 sprintf(name, ROW_START_PREFIX "%d", row);
1869 return string(name);
1872 string column_start_reg(int col)
1874 static char name[sizeof(COLUMN_START_PREFIX)+INT_DIGITS];
1875 sprintf(name, COLUMN_START_PREFIX "%d", col);
1876 return string(name);
1879 string column_end_reg(int col)
1881 static char name[sizeof(COLUMN_END_PREFIX)+INT_DIGITS];
1882 sprintf(name, COLUMN_END_PREFIX "%d", col);
1883 return string(name);
1886 string column_divide_reg(int col)
1888 static char name[sizeof(COLUMN_DIVIDE_PREFIX)+INT_DIGITS];
1889 sprintf(name, COLUMN_DIVIDE_PREFIX "%d", col);
1890 return string(name);
1893 string row_top_reg(int row)
1895 static char name[sizeof(ROW_TOP_PREFIX)+INT_DIGITS];
1896 sprintf(name, ROW_TOP_PREFIX "%d", row);
1897 return string(name);
1900 void init_span_reg(int start_col, int end_col)
1902 printfs(".nr %1 \\n(.H\n.nr %2 0\n.nr %3 0\n.nr %4 0\n",
1903 span_width_reg(start_col, end_col),
1904 span_alphabetic_width_reg(start_col, end_col),
1905 span_left_numeric_width_reg(start_col, end_col),
1906 span_right_numeric_width_reg(start_col, end_col));
1909 void compute_span_width(int start_col, int end_col)
1911 printfs(".nr %1 \\n[%1]>?(\\n[%2]+\\n[%3])\n"
1912 ".if \\n[%4] .nr %1 \\n[%1]>?(\\n[%4]+2n)\n",
1913 span_width_reg(start_col, end_col),
1914 span_left_numeric_width_reg(start_col, end_col),
1915 span_right_numeric_width_reg(start_col, end_col),
1916 span_alphabetic_width_reg(start_col, end_col));
1919 // Increase the widths of columns so that the width of any spanning entry
1920 // is not greater than the sum of the widths of the columns that it spans.
1921 // Ensure that the widths of columns remain equal.
1923 void table::divide_span(int start_col, int end_col)
1925 assert(end_col > start_col);
1926 printfs(".nr " NEEDED_REG " \\n[%1]-(\\n[%2]",
1927 span_width_reg(start_col, end_col),
1928 span_width_reg(start_col, start_col));
1929 int i;
1930 for (i = start_col + 1; i <= end_col; i++) {
1931 // The column separation may shrink with the expand option.
1932 if (!(flags & EXPAND))
1933 printfs("+%1n", as_string(column_separation[i - 1]));
1934 printfs("+\\n[%1]", span_width_reg(i, i));
1936 prints(")\n");
1937 printfs(".nr " NEEDED_REG " \\n[" NEEDED_REG "]/%1\n",
1938 as_string(end_col - start_col + 1));
1939 prints(".if \\n[" NEEDED_REG "] \\{");
1940 for (i = start_col; i <= end_col; i++)
1941 printfs(".nr %1 +\\n[" NEEDED_REG "]\n",
1942 span_width_reg(i, i));
1943 int equal_flag = 0;
1944 for (i = start_col; i <= end_col && !equal_flag; i++)
1945 if (equal[i])
1946 equal_flag = 1;
1947 if (equal_flag) {
1948 for (i = 0; i < ncolumns; i++)
1949 if (i < start_col || i > end_col)
1950 printfs(".nr %1 +\\n[" NEEDED_REG "]\n",
1951 span_width_reg(i, i));
1953 prints(".\\}\n");
1956 void table::sum_columns(int start_col, int end_col)
1958 assert(end_col > start_col);
1959 printfs(".nr %1 \\n[%2]",
1960 span_width_reg(start_col, end_col),
1961 span_width_reg(start_col, start_col));
1962 for (int i = start_col + 1; i <= end_col; i++)
1963 printfs("+(%1*\\n[" SEPARATION_FACTOR_REG "])+\\n[%2]",
1964 as_string(column_separation[i - 1]),
1965 span_width_reg(i, i));
1966 prints('\n');
1969 horizontal_span::horizontal_span(int sc, int ec, horizontal_span *p)
1970 : next(p), start_col(sc), end_col(ec)
1974 void table::build_span_list()
1976 span_list = 0;
1977 table_entry *p = entry_list;
1978 while (p) {
1979 if (p->end_col != p->start_col) {
1980 horizontal_span *q;
1981 for (q = span_list; q; q = q->next)
1982 if (q->start_col == p->start_col
1983 && q->end_col == p->end_col)
1984 break;
1985 if (!q)
1986 span_list = new horizontal_span(p->start_col, p->end_col, span_list);
1988 p = p->next;
1990 // Now sort span_list primarily by order of end_row, and secondarily
1991 // by reverse order of start_row. This ensures that if we divide
1992 // spans using the order in span_list, we will get reasonable results.
1993 horizontal_span *unsorted = span_list;
1994 span_list = 0;
1995 while (unsorted) {
1996 horizontal_span **pp;
1997 for (pp = &span_list; *pp; pp = &(*pp)->next)
1998 if (unsorted->end_col < (*pp)->end_col
1999 || (unsorted->end_col == (*pp)->end_col
2000 && (unsorted->start_col > (*pp)->start_col)))
2001 break;
2002 horizontal_span *tem = unsorted->next;
2003 unsorted->next = *pp;
2004 *pp = unsorted;
2005 unsorted = tem;
2009 void table::compute_separation_factor()
2011 if (flags & (ALLBOX|BOX|DOUBLEBOX))
2012 left_separation = right_separation = 1;
2013 else {
2014 for (int i = 0; i < nrows; i++) {
2015 if (vline[i][0] > 0)
2016 left_separation = 1;
2017 if (vline[i][ncolumns] > 0)
2018 right_separation = 1;
2021 if (flags & EXPAND) {
2022 int total_sep = left_separation + right_separation;
2023 int i;
2024 for (i = 0; i < ncolumns - 1; i++)
2025 total_sep += column_separation[i];
2026 if (total_sep != 0) {
2027 // Don't let the separation factor be negative.
2028 prints(".nr " SEPARATION_FACTOR_REG " \\n[.l]-\\n[.i]");
2029 for (i = 0; i < ncolumns; i++)
2030 printfs("-\\n[%1]", span_width_reg(i, i));
2031 printfs("/%1>?0\n", as_string(total_sep));
2036 void table::compute_column_positions()
2038 printfs(".nr %1 0\n", column_divide_reg(0));
2039 printfs(".nr %1 %2*\\n[" SEPARATION_FACTOR_REG "]\n",
2040 column_start_reg(0),
2041 as_string(left_separation));
2042 int i;
2043 for (i = 1;; i++) {
2044 printfs(".nr %1 \\n[%2]+\\n[%3]\n",
2045 column_end_reg(i-1),
2046 column_start_reg(i-1),
2047 span_width_reg(i-1, i-1));
2048 if (i >= ncolumns)
2049 break;
2050 printfs(".nr %1 \\n[%2]+(%3*\\n[" SEPARATION_FACTOR_REG "])\n",
2051 column_start_reg(i),
2052 column_end_reg(i-1),
2053 as_string(column_separation[i-1]));
2054 printfs(".nr %1 \\n[%2]+\\n[%3]/2\n",
2055 column_divide_reg(i),
2056 column_end_reg(i-1),
2057 column_start_reg(i));
2059 printfs(".nr %1 \\n[%2]+(%3*\\n[" SEPARATION_FACTOR_REG "])\n",
2060 column_divide_reg(ncolumns),
2061 column_end_reg(i-1),
2062 as_string(right_separation));
2063 printfs(".nr TW \\n[%1]\n",
2064 column_divide_reg(ncolumns));
2065 if (flags & DOUBLEBOX) {
2066 printfs(".nr %1 +" DOUBLE_LINE_SEP "\n", column_divide_reg(0));
2067 printfs(".nr %1 -" DOUBLE_LINE_SEP "\n", column_divide_reg(ncolumns));
2071 void table::make_columns_equal()
2073 int first = -1; // index of first equal column
2074 int i;
2075 for (i = 0; i < ncolumns; i++)
2076 if (equal[i]) {
2077 if (first < 0) {
2078 printfs(".nr %1 \\n[%1]", span_width_reg(i, i));
2079 first = i;
2081 else
2082 printfs(">?\\n[%1]", span_width_reg(i, i));
2084 if (first >= 0) {
2085 prints('\n');
2086 for (i = first + 1; i < ncolumns; i++)
2087 if (equal[i])
2088 printfs(".nr %1 \\n[%2]\n",
2089 span_width_reg(i, i),
2090 span_width_reg(first, first));
2094 void table::compute_widths()
2096 build_span_list();
2097 int i;
2098 horizontal_span *p;
2099 prints(".nr " SEPARATION_FACTOR_REG " 1n\n");
2100 for (i = 0; i < ncolumns; i++) {
2101 init_span_reg(i, i);
2102 if (!minimum_width[i].empty())
2103 printfs(".nr %1 %2\n", span_width_reg(i, i), minimum_width[i]);
2105 for (p = span_list; p; p = p->next)
2106 init_span_reg(p->start_col, p->end_col);
2107 table_entry *q;
2108 for (q = entry_list; q; q = q->next)
2109 if (!q->mod->zero_width)
2110 q->do_width();
2111 for (i = 0; i < ncolumns; i++)
2112 compute_span_width(i, i);
2113 for (p = span_list; p; p = p->next)
2114 compute_span_width(p->start_col, p->end_col);
2115 make_columns_equal();
2116 // Note that divide_span keeps equal width columns equal.
2117 for (p = span_list; p; p = p->next)
2118 divide_span(p->start_col, p->end_col);
2119 for (p = span_list; p; p = p->next)
2120 sum_columns(p->start_col, p->end_col);
2121 int had_spanning_block = 0;
2122 int had_equal_block = 0;
2123 for (q = entry_list; q; q = q->next)
2124 if (q->divert(ncolumns, minimum_width,
2125 (flags & EXPAND) ? column_separation : 0)) {
2126 if (q->end_col > q->start_col)
2127 had_spanning_block = 1;
2128 for (i = q->start_col; i <= q->end_col && !had_equal_block; i++)
2129 if (equal[i])
2130 had_equal_block = 1;
2132 if (had_equal_block)
2133 make_columns_equal();
2134 if (had_spanning_block)
2135 for (p = span_list; p; p = p->next)
2136 divide_span(p->start_col, p->end_col);
2137 compute_separation_factor();
2138 for (p = span_list; p; p = p->next)
2139 sum_columns(p->start_col, p->end_col);
2140 compute_column_positions();
2143 void table::print_single_hline(int r)
2145 prints(".vs " LINE_SEP ">?\\n[.V]u\n"
2146 ".ls 1\n"
2147 "\\v'" BODY_DEPTH "'"
2148 "\\s[\\n[" LINESIZE_REG "]]");
2149 if (r > nrows - 1)
2150 prints("\\D'l |\\n[TW]u 0'");
2151 else {
2152 int start_col = 0;
2153 for (;;) {
2154 while (start_col < ncolumns
2155 && entry[r][start_col] != 0
2156 && entry[r][start_col]->start_row != r)
2157 start_col++;
2158 int end_col;
2159 for (end_col = start_col;
2160 end_col < ncolumns
2161 && (entry[r][end_col] == 0
2162 || entry[r][end_col]->start_row == r);
2163 end_col++)
2165 if (end_col <= start_col)
2166 break;
2167 printfs("\\h'|\\n[%1]u",
2168 column_divide_reg(start_col));
2169 if ((r > 0 && vline[r-1][start_col] == 2)
2170 || (r < nrows && vline[r][start_col] == 2))
2171 prints("-" HALF_DOUBLE_LINE_SEP);
2172 prints("'");
2173 printfs("\\D'l |\\n[%1]u",
2174 column_divide_reg(end_col));
2175 if ((r > 0 && vline[r-1][end_col] == 2)
2176 || (r < nrows && vline[r][end_col] == 2))
2177 prints("+" HALF_DOUBLE_LINE_SEP);
2178 prints(" 0'");
2179 start_col = end_col;
2182 prints("\\s0\n");
2183 prints(".ls\n"
2184 ".vs\n");
2187 void table::print_double_hline(int r)
2189 prints(".vs " LINE_SEP "+" DOUBLE_LINE_SEP
2190 ">?\\n[.V]u\n"
2191 ".ls 1\n"
2192 "\\v'" BODY_DEPTH "'"
2193 "\\s[\\n[" LINESIZE_REG "]]");
2194 if (r > nrows - 1)
2195 prints("\\v'-" DOUBLE_LINE_SEP "'"
2196 "\\D'l |\\n[TW]u 0'"
2197 "\\v'" DOUBLE_LINE_SEP "'"
2198 "\\h'|0'"
2199 "\\D'l |\\n[TW]u 0'");
2200 else {
2201 int start_col = 0;
2202 for (;;) {
2203 while (start_col < ncolumns
2204 && entry[r][start_col] != 0
2205 && entry[r][start_col]->start_row != r)
2206 start_col++;
2207 int end_col;
2208 for (end_col = start_col;
2209 end_col < ncolumns
2210 && (entry[r][end_col] == 0
2211 || entry[r][end_col]->start_row == r);
2212 end_col++)
2214 if (end_col <= start_col)
2215 break;
2216 const char *left_adjust = 0;
2217 if ((r > 0 && vline[r-1][start_col] == 2)
2218 || (r < nrows && vline[r][start_col] == 2))
2219 left_adjust = "-" HALF_DOUBLE_LINE_SEP;
2220 const char *right_adjust = 0;
2221 if ((r > 0 && vline[r-1][end_col] == 2)
2222 || (r < nrows && vline[r][end_col] == 2))
2223 right_adjust = "+" HALF_DOUBLE_LINE_SEP;
2224 printfs("\\v'-" DOUBLE_LINE_SEP "'"
2225 "\\h'|\\n[%1]u",
2226 column_divide_reg(start_col));
2227 if (left_adjust)
2228 prints(left_adjust);
2229 prints("'");
2230 printfs("\\D'l |\\n[%1]u",
2231 column_divide_reg(end_col));
2232 if (right_adjust)
2233 prints(right_adjust);
2234 prints(" 0'");
2235 printfs("\\v'" DOUBLE_LINE_SEP "'"
2236 "\\h'|\\n[%1]u",
2237 column_divide_reg(start_col));
2238 if (left_adjust)
2239 prints(left_adjust);
2240 prints("'");
2241 printfs("\\D'l |\\n[%1]u",
2242 column_divide_reg(end_col));
2243 if (right_adjust)
2244 prints(right_adjust);
2245 prints(" 0'");
2246 start_col = end_col;
2249 prints("\\s0\n"
2250 ".ls\n"
2251 ".vs\n");
2254 void table::compute_vrule_top_adjust(int start_row, int col, string &result)
2256 if (row_is_all_lines[start_row] && start_row < nrows - 1) {
2257 if (row_is_all_lines[start_row] == 2)
2258 result = LINE_SEP ">?\\n[.V]u" "+" DOUBLE_LINE_SEP;
2259 else
2260 result = LINE_SEP ">?\\n[.V]u";
2261 start_row++;
2263 else {
2264 result = "";
2265 if (start_row == 0)
2266 return;
2267 for (stuff *p = stuff_list; p && p->row <= start_row; p = p->next)
2268 if (p->row == start_row
2269 && (p->is_single_line() || p->is_double_line()))
2270 return;
2272 int left = 0;
2273 if (col > 0) {
2274 table_entry *e = entry[start_row-1][col-1];
2275 if (e && e->start_row == e->end_row) {
2276 if (e->to_double_line_entry() != 0)
2277 left = 2;
2278 else if (e->to_single_line_entry() != 0)
2279 left = 1;
2282 int right = 0;
2283 if (col < ncolumns) {
2284 table_entry *e = entry[start_row-1][col];
2285 if (e && e->start_row == e->end_row) {
2286 if (e->to_double_line_entry() != 0)
2287 right = 2;
2288 else if (e->to_single_line_entry() != 0)
2289 right = 1;
2292 if (row_is_all_lines[start_row-1] == 0) {
2293 if (left > 0 || right > 0) {
2294 result += "-" BODY_DEPTH "-" BAR_HEIGHT;
2295 if ((left == 2 && right != 2) || (right == 2 && left != 2))
2296 result += "-" HALF_DOUBLE_LINE_SEP;
2297 else if (left == 2 && right == 2)
2298 result += "+" HALF_DOUBLE_LINE_SEP;
2301 else if (row_is_all_lines[start_row-1] == 2) {
2302 if ((left == 2 && right != 2) || (right == 2 && left != 2))
2303 result += "-" DOUBLE_LINE_SEP;
2304 else if (left == 1 || right == 1)
2305 result += "-" HALF_DOUBLE_LINE_SEP;
2309 void table::compute_vrule_bot_adjust(int end_row, int col, string &result)
2311 if (row_is_all_lines[end_row] && end_row > 0) {
2312 end_row--;
2313 result = "";
2315 else {
2316 stuff *p;
2317 for (p = stuff_list; p && p->row < end_row + 1; p = p->next)
2319 if (p && p->row == end_row + 1 && p->is_double_line()) {
2320 result = "-" DOUBLE_LINE_SEP;
2321 return;
2323 if ((p != 0 && p->row == end_row + 1)
2324 || end_row == nrows - 1) {
2325 result = "";
2326 return;
2328 if (row_is_all_lines[end_row+1] == 1)
2329 result = LINE_SEP;
2330 else if (row_is_all_lines[end_row+1] == 2)
2331 result = LINE_SEP "+" DOUBLE_LINE_SEP;
2332 else
2333 result = "";
2335 int left = 0;
2336 if (col > 0) {
2337 table_entry *e = entry[end_row+1][col-1];
2338 if (e && e->start_row == e->end_row) {
2339 if (e->to_double_line_entry() != 0)
2340 left = 2;
2341 else if (e->to_single_line_entry() != 0)
2342 left = 1;
2345 int right = 0;
2346 if (col < ncolumns) {
2347 table_entry *e = entry[end_row+1][col];
2348 if (e && e->start_row == e->end_row) {
2349 if (e->to_double_line_entry() != 0)
2350 right = 2;
2351 else if (e->to_single_line_entry() != 0)
2352 right = 1;
2355 if (row_is_all_lines[end_row+1] == 0) {
2356 if (left > 0 || right > 0) {
2357 result = "1v-" BODY_DEPTH "-" BAR_HEIGHT;
2358 if ((left == 2 && right != 2) || (right == 2 && left != 2))
2359 result += "+" HALF_DOUBLE_LINE_SEP;
2360 else if (left == 2 && right == 2)
2361 result += "-" HALF_DOUBLE_LINE_SEP;
2364 else if (row_is_all_lines[end_row+1] == 2) {
2365 if (left == 2 && right == 2)
2366 result += "-" DOUBLE_LINE_SEP;
2367 else if (left != 2 && right != 2 && (left == 1 || right == 1))
2368 result += "-" HALF_DOUBLE_LINE_SEP;
2372 void table::add_vertical_rule(int start_row, int end_row, int col, int is_double)
2374 vrule_list = new vertical_rule(start_row, end_row, col, is_double,
2375 vrule_list);
2376 compute_vrule_top_adjust(start_row, col, vrule_list->top_adjust);
2377 compute_vrule_bot_adjust(end_row, col, vrule_list->bot_adjust);
2380 void table::build_vrule_list()
2382 int col;
2383 if (flags & ALLBOX) {
2384 for (col = 1; col < ncolumns; col++) {
2385 int start_row = 0;
2386 for (;;) {
2387 while (start_row < nrows && vline_spanned(start_row, col))
2388 start_row++;
2389 if (start_row >= nrows)
2390 break;
2391 int end_row = start_row;
2392 while (end_row < nrows && !vline_spanned(end_row, col))
2393 end_row++;
2394 end_row--;
2395 add_vertical_rule(start_row, end_row, col, 0);
2396 start_row = end_row + 1;
2400 if (flags & (BOX|ALLBOX|DOUBLEBOX)) {
2401 add_vertical_rule(0, nrows - 1, 0, 0);
2402 add_vertical_rule(0, nrows - 1, ncolumns, 0);
2404 for (int end_row = 0; end_row < nrows; end_row++)
2405 for (col = 0; col < ncolumns+1; col++)
2406 if (vline[end_row][col] > 0
2407 && !vline_spanned(end_row, col)
2408 && (end_row == nrows - 1
2409 || vline[end_row+1][col] != vline[end_row][col]
2410 || vline_spanned(end_row+1, col))) {
2411 int start_row;
2412 for (start_row = end_row - 1;
2413 start_row >= 0
2414 && vline[start_row][col] == vline[end_row][col]
2415 && !vline_spanned(start_row, col);
2416 start_row--)
2418 start_row++;
2419 add_vertical_rule(start_row, end_row, col, vline[end_row][col] > 1);
2421 for (vertical_rule *p = vrule_list; p; p = p->next)
2422 if (p->is_double)
2423 for (int r = p->start_row; r <= p->end_row; r++) {
2424 if (p->col > 0 && entry[r][p->col-1] != 0
2425 && entry[r][p->col-1]->end_col == p->col-1) {
2426 int is_corner = r == p->start_row || r == p->end_row;
2427 entry[r][p->col-1]->note_double_vrule_on_right(is_corner);
2429 if (p->col < ncolumns && entry[r][p->col] != 0
2430 && entry[r][p->col]->start_col == p->col) {
2431 int is_corner = r == p->start_row || r == p->end_row;
2432 entry[r][p->col]->note_double_vrule_on_left(is_corner);
2437 void table::define_bottom_macro()
2439 prints(".eo\n"
2440 ".de T#\n"
2441 ".if !\\n[" SUPPRESS_BOTTOM_REG "] \\{"
2442 "." REPEATED_VPT_MACRO " 0\n"
2443 ".mk " SAVED_VERTICAL_POS_REG "\n");
2444 if (flags & (BOX|ALLBOX|DOUBLEBOX)) {
2445 prints(".if \\n[T.]&\\n[" NEED_BOTTOM_RULE_REG "] \\{");
2446 print_single_hline(0);
2447 prints(".\\}\n");
2449 prints(".ls 1\n");
2450 for (vertical_rule *p = vrule_list; p; p = p->next)
2451 p->contribute_to_bottom_macro(this);
2452 if (flags & DOUBLEBOX)
2453 prints(".if \\n[T.] \\{.vs " DOUBLE_LINE_SEP ">?\\n[.V]u\n"
2454 "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]"
2455 "\\D'l \\n[TW]u 0'\\s0\n"
2456 ".vs\n"
2457 ".\\}\n"
2458 ".if \\n[" LAST_PASSED_ROW_REG "]>=0 "
2459 ".nr " TOP_REG " \\n[#T]-" DOUBLE_LINE_SEP "\n"
2460 ".sp -1\n"
2461 "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]"
2462 "\\D'l 0 |\\n[" TOP_REG "]u-1v'\\s0\n"
2463 ".sp -1\n"
2464 "\\v'" BODY_DEPTH "'\\h'|\\n[TW]u'\\s[\\n[" LINESIZE_REG "]]"
2465 "\\D'l 0 |\\n[" TOP_REG "]u-1v'\\s0\n");
2466 prints(".ls\n");
2467 prints(".nr " LAST_PASSED_ROW_REG " \\n[" CURRENT_ROW_REG "]\n"
2468 ".sp |\\n[" SAVED_VERTICAL_POS_REG "]u\n"
2469 "." REPEATED_VPT_MACRO " 1\n"
2470 ".\\}\n"
2471 "..\n"
2472 ".ec\n");
2475 // is the vertical line before column c in row r horizontally spanned?
2477 int table::vline_spanned(int r, int c)
2479 assert(r >= 0 && r < nrows && c >= 0 && c < ncolumns + 1);
2480 return (c != 0 && c != ncolumns && entry[r][c] != 0
2481 && entry[r][c]->start_col != c
2482 // horizontally spanning lines don't count
2483 && entry[r][c]->to_double_line_entry() == 0
2484 && entry[r][c]->to_single_line_entry() == 0);
2487 int table::row_begins_section(int r)
2489 assert(r >= 0 && r < nrows);
2490 for (int i = 0; i < ncolumns; i++)
2491 if (entry[r][i] && entry[r][i]->start_row != r)
2492 return 0;
2493 return 1;
2496 int table::row_ends_section(int r)
2498 assert(r >= 0 && r < nrows);
2499 for (int i = 0; i < ncolumns; i++)
2500 if (entry[r][i] && entry[r][i]->end_row != r)
2501 return 0;
2502 return 1;
2505 void table::do_row(int r)
2507 if (!(flags & NOKEEP) && row_begins_section(r))
2508 prints("." KEEP_MACRO_NAME "\n");
2509 int had_line = 0;
2510 stuff *p;
2511 for (p = stuff_list; p && p->row < r; p = p->next)
2513 for (stuff *p1 = p; p1 && p1->row == r; p1 = p1->next)
2514 if (!p1->printed && (p1->is_single_line() || p1->is_double_line())) {
2515 had_line = 1;
2516 break;
2518 if (!had_line && !row_is_all_lines[r])
2519 printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r));
2520 had_line = 0;
2521 for (; p && p->row == r; p = p->next)
2522 if (!p->printed) {
2523 p->print(this);
2524 if (!had_line && (p->is_single_line() || p->is_double_line())) {
2525 printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r));
2526 had_line = 1;
2529 // Change the row *after* printing the stuff list (which might contain .TH).
2530 printfs("\\*[" TRANSPARENT_STRING_NAME "].nr " CURRENT_ROW_REG " %1\n",
2531 as_string(r));
2532 if (!had_line && row_is_all_lines[r])
2533 printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r));
2534 // we might have had a .TH, for example, since we last tried
2535 if (!(flags & NOKEEP) && row_begins_section(r))
2536 prints("." KEEP_MACRO_NAME "\n");
2537 printfs(".mk %1\n", row_start_reg(r));
2538 prints(".mk " BOTTOM_REG "\n"
2539 "." REPEATED_VPT_MACRO " 0\n");
2540 int c;
2541 int row_is_blank = 1;
2542 int first_start_row = r;
2543 for (c = 0; c < ncolumns; c++) {
2544 table_entry *e = entry[r][c];
2545 if (e) {
2546 if (e->end_row == r) {
2547 e->do_depth();
2548 if (e->start_row < first_start_row)
2549 first_start_row = e->start_row;
2550 row_is_blank = 0;
2552 c = e->end_col;
2555 if (row_is_blank)
2556 prints(".nr " BOTTOM_REG " +1v\n");
2557 if (row_is_all_lines[r]) {
2558 prints(".vs " LINE_SEP);
2559 if (row_is_all_lines[r] == 2)
2560 prints("+" DOUBLE_LINE_SEP);
2561 prints(">?\\n[.V]u\n.ls 1\n");
2562 prints("\\&");
2563 prints("\\v'" BODY_DEPTH);
2564 if (row_is_all_lines[r] == 2)
2565 prints("-" HALF_DOUBLE_LINE_SEP);
2566 prints("'");
2567 for (c = 0; c < ncolumns; c++) {
2568 table_entry *e = entry[r][c];
2569 if (e) {
2570 if (e->end_row == e->start_row)
2571 e->to_simple_entry()->simple_print(1);
2572 c = e->end_col;
2575 prints("\n");
2576 prints(".ls\n"
2577 ".vs\n");
2578 prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n");
2579 printfs(".sp |\\n[%1]u\n", row_start_reg(r));
2581 for (int i = row_is_all_lines[r] ? r - 1 : r;
2582 i >= first_start_row;
2583 i--) {
2584 simple_entry *first = 0;
2585 for (c = 0; c < ncolumns; c++) {
2586 table_entry *e = entry[r][c];
2587 if (e) {
2588 if (e->end_row == r && e->start_row == i) {
2589 simple_entry *simple = e->to_simple_entry();
2590 if (simple) {
2591 if (!first) {
2592 prints(".ta");
2593 first = simple;
2595 simple->add_tab();
2598 c = e->end_col;
2601 if (first) {
2602 prints('\n');
2603 first->position_vertically();
2604 first->set_location();
2605 prints("\\&");
2606 first->simple_print(0);
2607 for (c = first->end_col + 1; c < ncolumns; c++) {
2608 table_entry *e = entry[r][c];
2609 if (e) {
2610 if (e->end_row == r && e->start_row == i) {
2611 simple_entry *simple = e->to_simple_entry();
2612 if (simple) {
2613 if (e->end_row != e->start_row) {
2614 prints('\n');
2615 simple->position_vertically();
2616 prints("\\&");
2618 simple->simple_print(0);
2621 c = e->end_col;
2624 prints('\n');
2625 prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n");
2626 printfs(".sp |\\n[%1]u\n", row_start_reg(r));
2629 for (c = 0; c < ncolumns; c++) {
2630 table_entry *e = entry[r][c];
2631 if (e) {
2632 if (e->end_row == r && e->to_simple_entry() == 0) {
2633 e->position_vertically();
2634 e->print();
2635 prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n");
2636 printfs(".sp |\\n[%1]u\n", row_start_reg(r));
2638 c = e->end_col;
2641 prints("." REPEATED_VPT_MACRO " 1\n"
2642 ".sp |\\n[" BOTTOM_REG "]u\n"
2643 "\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG " 1\n");
2644 if (r != nrows - 1 && (flags & ALLBOX)) {
2645 print_single_hline(r + 1);
2646 prints("\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG " 0\n");
2648 if (r != nrows - 1) {
2649 if (p && p->row == r + 1
2650 && (p->is_single_line() || p->is_double_line())) {
2651 p->print(this);
2652 prints("\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG
2653 " 0\n");
2655 int printed_one = 0;
2656 for (vertical_rule *vr = vrule_list; vr; vr = vr->next)
2657 if (vr->end_row == r) {
2658 if (!printed_one) {
2659 prints("." REPEATED_VPT_MACRO " 0\n");
2660 printed_one = 1;
2662 vr->print();
2664 if (printed_one)
2665 prints("." REPEATED_VPT_MACRO " 1\n");
2666 if (!(flags & NOKEEP) && row_ends_section(r))
2667 prints("." RELEASE_MACRO_NAME "\n");
2671 void table::do_top()
2673 prints(".fc \002\003\n");
2674 if (!(flags & NOKEEP) && (flags & (BOX|DOUBLEBOX|ALLBOX)))
2675 prints("." TABLE_KEEP_MACRO_NAME "\n");
2676 if (flags & DOUBLEBOX) {
2677 prints(".ls 1\n"
2678 ".vs " LINE_SEP ">?\\n[.V]u\n"
2679 "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]\\D'l \\n[TW]u 0'\\s0\n"
2680 ".vs\n"
2681 "." REPEATED_MARK_MACRO " " TOP_REG "\n"
2682 ".vs " DOUBLE_LINE_SEP ">?\\n[.V]u\n");
2683 printfs("\\v'" BODY_DEPTH "'"
2684 "\\s[\\n[" LINESIZE_REG "]]"
2685 "\\h'\\n[%1]u'"
2686 "\\D'l |\\n[%2]u 0'"
2687 "\\s0"
2688 "\n",
2689 column_divide_reg(0),
2690 column_divide_reg(ncolumns));
2691 prints(".ls\n"
2692 ".vs\n");
2694 else if (flags & (ALLBOX|BOX)) {
2695 print_single_hline(0);
2697 //printfs(".mk %1\n", row_top_reg(0));
2700 void table::do_bottom()
2702 // print stuff after last row
2703 for (stuff *p = stuff_list; p; p = p->next)
2704 if (p->row > nrows - 1)
2705 p->print(this);
2706 if (!(flags & NOKEEP))
2707 prints("." RELEASE_MACRO_NAME "\n");
2708 printfs(".mk %1\n", row_top_reg(nrows));
2709 prints(".nr " NEED_BOTTOM_RULE_REG " 1\n"
2710 ".nr T. 1\n"
2711 ".T#\n");
2712 if (!(flags & NOKEEP) && (flags & (BOX|DOUBLEBOX|ALLBOX)))
2713 prints("." TABLE_RELEASE_MACRO_NAME "\n");
2714 if (flags & DOUBLEBOX)
2715 prints(".sp " DOUBLE_LINE_SEP "\n");
2716 prints("." RESET_MACRO_NAME "\n"
2717 ".fc\n"
2718 ".cp \\n(" COMPATIBLE_REG "\n");
2721 int table::get_nrows()
2723 return nrows;
2726 const char *last_filename = 0;
2728 void set_troff_location(const char *fn, int ln)
2730 if (!location_force_filename && last_filename != 0
2731 && strcmp(fn, last_filename) == 0)
2732 printfs(".lf %1\n", as_string(ln));
2733 else {
2734 printfs(".lf %1 %2\n", as_string(ln), fn);
2735 last_filename = fn;
2736 location_force_filename = 0;
2740 void printfs(const char *s, const string &arg1, const string &arg2,
2741 const string &arg3, const string &arg4, const string &arg5)
2743 if (s) {
2744 char c;
2745 while ((c = *s++) != '\0') {
2746 if (c == '%') {
2747 switch (*s++) {
2748 case '1':
2749 prints(arg1);
2750 break;
2751 case '2':
2752 prints(arg2);
2753 break;
2754 case '3':
2755 prints(arg3);
2756 break;
2757 case '4':
2758 prints(arg4);
2759 break;
2760 case '5':
2761 prints(arg5);
2762 break;
2763 case '6':
2764 case '7':
2765 case '8':
2766 case '9':
2767 break;
2768 case '%':
2769 prints('%');
2770 break;
2771 default:
2772 assert(0);
2775 else
2776 prints(c);