groff before CVS: release 1.06
[s-roff.git] / tbl / table.cc
blobca975395e8150732e58551795549efc9b69f1e16
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 BODY_DEPTH ".25m"
27 #define BODY_HEIGHT ".75m"
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"
79 #define TEXT_STRING_PREFIX PREFIX "s"
80 #define RIGHT_TEXT_STRING_PREFIX PREFIX "ss"
82 string block_width_reg(int r, int c);
83 string block_diversion_name(int r, int c);
84 string block_height_reg(int r, int c);
85 string span_width_reg(int start_col, int end_col);
86 string span_left_numeric_width_reg(int start_col, int end_col);
87 string span_right_numeric_width_reg(int start_col, int end_col);
88 string span_alphabetic_width_reg(int start_col, int end_col);
89 string column_separation_reg(int col);
90 string row_start_reg(int r);
91 string column_start_reg(int c);
92 string column_end_reg(int c);
93 string column_divide_reg(int c);
94 string row_top_reg(int r);
95 string text_string_name(int r, int c);
96 string right_text_string_name(int r, int c);
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_dot(const char *s, const char *delim);
103 string an_empty_string;
104 int location_force_filename = 0;
106 void printfs(const char *,
107 const string &arg1 = an_empty_string,
108 const string &arg2 = an_empty_string,
109 const string &arg3 = an_empty_string,
110 const string &arg4 = an_empty_string,
111 const string &arg5 = an_empty_string);
113 void prints(const char *);
114 void prints(const string &);
115 void prints(char);
117 inline void prints(char c)
119 putchar(c);
122 inline void prints(const char *s)
124 fputs(s, stdout);
127 void prints(const string &s)
129 if (!s.empty())
130 fwrite(s.contents(), 1, s.length(), stdout);
133 struct horizontal_span {
134 horizontal_span *next;
135 short start_col;
136 short end_col;
137 horizontal_span(int, int, horizontal_span *);
140 struct single_line_entry;
141 struct double_line_entry;
142 struct simple_entry;
144 class table_entry {
145 friend class table;
146 table_entry *next;
147 int input_lineno;
148 const char *input_filename;
149 protected:
150 short start_row;
151 short start_col;
152 short end_row;
153 short end_col;
154 const entry_modifier *mod;
155 public:
156 void set_location();
157 table_entry(const entry_modifier *);
158 virtual ~table_entry();
159 virtual int divert(int ncols, const string *mw);
160 virtual void do_width();
161 virtual void do_depth();
162 virtual void print() = 0;
163 virtual void position_vertically() = 0;
164 virtual single_line_entry *to_single_line_entry();
165 virtual double_line_entry *to_double_line_entry();
166 virtual simple_entry *to_simple_entry();
167 virtual int line_type();
168 virtual void note_double_vrule_on_right(int);
169 virtual void note_double_vrule_on_left(int);
172 class simple_entry : public table_entry {
173 public:
174 simple_entry(const entry_modifier *);
175 void print();
176 void position_vertically();
177 simple_entry *to_simple_entry();
178 virtual void add_tab();
179 virtual void simple_print(int);
182 class empty_entry : public simple_entry {
183 public:
184 empty_entry(const entry_modifier *);
185 int line_type();
188 class text_entry : public simple_entry {
189 protected:
190 char *contents;
191 public:
192 text_entry(char *, const entry_modifier *);
193 ~text_entry();
196 class repeated_char_entry : public text_entry {
197 public:
198 repeated_char_entry(char *s, const entry_modifier *m);
199 void simple_print(int);
202 class simple_text_entry : public text_entry {
203 public:
204 simple_text_entry(char *s, const entry_modifier *m);
205 void do_width();
208 class left_text_entry : public simple_text_entry {
209 public:
210 left_text_entry(char *s, const entry_modifier *m);
211 void simple_print(int);
212 void add_tab();
215 class right_text_entry : public simple_text_entry {
216 public:
217 right_text_entry(char *s, const entry_modifier *m);
218 void simple_print(int);
219 void add_tab();
222 class center_text_entry : public simple_text_entry {
223 public:
224 center_text_entry(char *s, const entry_modifier *m);
225 void simple_print(int);
226 void add_tab();
229 class numeric_text_entry : public text_entry {
230 int dot_pos;
231 public:
232 numeric_text_entry(char *s, const entry_modifier *m, int pos);
233 void do_width();
234 void simple_print(int);
237 class alphabetic_text_entry : public text_entry {
238 public:
239 alphabetic_text_entry(char *s, const entry_modifier *m);
240 void do_width();
241 void simple_print(int);
242 void add_tab();
245 class line_entry : public simple_entry {
246 protected:
247 char double_vrule_on_right;
248 char double_vrule_on_left;
249 public:
250 line_entry(const entry_modifier *);
251 void note_double_vrule_on_right(int);
252 void note_double_vrule_on_left(int);
253 void simple_print(int) = 0;
256 class single_line_entry : public line_entry {
257 public:
258 single_line_entry(const entry_modifier *m);
259 void simple_print(int);
260 single_line_entry *to_single_line_entry();
261 int line_type();
264 class double_line_entry : public line_entry {
265 public:
266 double_line_entry(const entry_modifier *m);
267 void simple_print(int);
268 double_line_entry *to_double_line_entry();
269 int line_type();
272 class short_line_entry : public simple_entry {
273 public:
274 short_line_entry(const entry_modifier *m);
275 void simple_print(int);
276 int line_type();
279 class short_double_line_entry : public simple_entry {
280 public:
281 short_double_line_entry(const entry_modifier *m);
282 void simple_print(int);
283 int line_type();
286 class block_entry : public table_entry {
287 char *contents;
288 protected:
289 void do_divert(int alphabetic, int ncols, const string *mw);
290 public:
291 block_entry(char *s, const entry_modifier *m);
292 ~block_entry();
293 int divert(int ncols, const string *mw);
294 void do_width();
295 void do_depth();
296 void position_vertically();
297 void print() = 0;
300 class left_block_entry : public block_entry {
301 public:
302 left_block_entry(char *s, const entry_modifier *m);
303 void print();
306 class right_block_entry : public block_entry {
307 public:
308 right_block_entry(char *s, const entry_modifier *m);
309 void print();
312 class center_block_entry : public block_entry {
313 public:
314 center_block_entry(char *s, const entry_modifier *m);
315 void print();
318 class alphabetic_block_entry : public block_entry {
319 public:
320 alphabetic_block_entry(char *s, const entry_modifier *m);
321 void print();
322 int divert(int ncols, const string *mw);
325 table_entry::table_entry(const entry_modifier *m)
326 : next(0), start_row(-1), end_row(-1), start_col(-1), end_col(-1), mod(m),
327 input_lineno(-1), input_filename(0)
331 table_entry::~table_entry()
335 int table_entry::divert(int, const string *)
337 return 0;
340 void table_entry::do_width()
344 single_line_entry *table_entry::to_single_line_entry()
346 return 0;
349 double_line_entry *table_entry::to_double_line_entry()
351 return 0;
354 simple_entry *table_entry::to_simple_entry()
356 return 0;
359 void table_entry::do_depth()
363 void table_entry::set_location()
365 set_troff_location(input_filename, input_lineno);
368 int table_entry::line_type()
370 return -1;
373 void table_entry::note_double_vrule_on_right(int)
377 void table_entry::note_double_vrule_on_left(int)
381 simple_entry::simple_entry(const entry_modifier *m) : table_entry(m)
385 void simple_entry::add_tab()
387 // do nothing
390 void simple_entry::simple_print(int)
392 // do nothing
395 void simple_entry::position_vertically()
397 if (start_row != end_row)
398 switch (mod->vertical_alignment) {
399 case entry_modifier::TOP:
400 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
401 break;
402 case entry_modifier::CENTER:
403 // Peform the motion in two stages so that the center is rounded
404 // vertically upwards even if net vertical motion is upwards.
405 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
406 printfs(".sp \\n[" BOTTOM_REG "]u-\\n[%1]u-1v/2u\n",
407 row_start_reg(start_row));
408 break;
409 case entry_modifier::BOTTOM:
410 printfs(".sp |\\n[%1]u+\\n[" BOTTOM_REG "]u-\\n[%1]u-1v\n",
411 row_start_reg(start_row));
412 break;
413 default:
414 assert(0);
418 void simple_entry::print()
420 prints(".ta");
421 add_tab();
422 prints('\n');
423 set_location();
424 prints("\\&");
425 simple_print(0);
426 prints('\n');
429 simple_entry *simple_entry::to_simple_entry()
431 return this;
434 empty_entry::empty_entry(const entry_modifier *m)
435 : simple_entry(m)
439 int empty_entry::line_type()
441 return 0;
444 text_entry::text_entry(char *s, const entry_modifier *m)
445 : contents(s), simple_entry(m)
449 text_entry::~text_entry()
451 a_delete contents;
455 repeated_char_entry::repeated_char_entry(char *s, const entry_modifier *m)
456 : text_entry(s, m)
460 void repeated_char_entry::simple_print(int)
462 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
463 set_inline_modifier(mod);
464 printfs("\\l" DELIMITER_CHAR "\\n[%1]u\\&", span_width_reg(start_col, end_col));
465 prints(contents);
466 prints(DELIMITER_CHAR);
467 restore_inline_modifier(mod);
470 simple_text_entry::simple_text_entry(char *s, const entry_modifier *m)
471 : text_entry(s, m)
475 void simple_text_entry::do_width()
477 printfs(".ds %1 \"", text_string_name(start_row, start_col));
478 set_inline_modifier(mod);
479 prints(contents);
480 restore_inline_modifier(mod);
481 prints('\n');
482 set_location();
483 printfs(".nr %1 \\n[%1]>?\\w'\\*[%2]'\n",
484 span_width_reg(start_col, end_col),
485 text_string_name(start_row, start_col));
488 left_text_entry::left_text_entry(char *s, const entry_modifier *m)
489 : simple_text_entry(s, m)
493 void left_text_entry::simple_print(int)
495 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
496 printfs("\\*[%1]", text_string_name(start_row, start_col));
499 // The only point of this is to make `\a' ``work'' as in Unix tbl. Grrr.
501 void left_text_entry::add_tab()
503 printfs(" \\n[%1]u", column_end_reg(end_col));
506 right_text_entry::right_text_entry(char *s, const entry_modifier *m)
507 : simple_text_entry(s, m)
511 void right_text_entry::simple_print(int)
513 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
514 printfs("\002\003\\*[%1]\002", text_string_name(start_row, start_col));
517 void right_text_entry::add_tab()
519 printfs(" \\n[%1]u", column_end_reg(end_col));
522 center_text_entry::center_text_entry(char *s, const entry_modifier *m)
523 : simple_text_entry(s, m)
527 void center_text_entry::simple_print(int)
529 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
530 printfs("\002\003\\*[%1]\003\002", text_string_name(start_row, start_col));
533 void center_text_entry::add_tab()
535 printfs(" \\n[%1]u", column_end_reg(end_col));
538 numeric_text_entry::numeric_text_entry(char *s, const entry_modifier *m, int pos)
539 : text_entry(s, m), dot_pos(pos)
543 void numeric_text_entry::do_width()
545 if (dot_pos != 0) {
546 printfs(".ds %1 \"", text_string_name(start_row, start_col));
547 set_inline_modifier(mod);
548 for (int i = 0; i < dot_pos; i++)
549 prints(contents[i]);
550 restore_inline_modifier(mod);
551 prints('\n');
552 set_location();
553 printfs(".nr %1 0\\w'\\*[%2]'\n",
554 block_width_reg(start_row, start_col),
555 text_string_name(start_row, start_col));
556 printfs(".nr %1 \\n[%1]>?\\n[%2]\n",
557 span_left_numeric_width_reg(start_col, end_col),
558 block_width_reg(start_row, start_col));
560 else
561 printfs(".nr %1 0\n", block_width_reg(start_row, start_col));
562 if (contents[dot_pos] != '\0') {
563 printfs(".ds %1 \"", right_text_string_name(start_row, start_col));
564 set_inline_modifier(mod);
565 prints(contents + dot_pos);
566 restore_inline_modifier(mod);
567 prints('\n');
568 set_location();
569 printfs(".nr %1 \\n[%1]>?\\w'\\*[%2]'\n",
570 span_right_numeric_width_reg(start_col, end_col),
571 right_text_string_name(start_row, start_col));
575 void numeric_text_entry::simple_print(int)
577 printfs("\\h'|(\\n[%1]u-\\n[%2]u-\\n[%3]u/2u+\\n[%2]u+\\n[%4]u-\\n[%5]u)'",
578 span_width_reg(start_col, end_col),
579 span_left_numeric_width_reg(start_col, end_col),
580 span_right_numeric_width_reg(start_col, end_col),
581 column_start_reg(start_col),
582 block_width_reg(start_row, start_col));
583 if (dot_pos != 0) {
584 printfs("\\*[%1]", text_string_name(start_row, start_col));
586 if (contents[dot_pos] != '\0')
587 printfs("\\*[%1]", right_text_string_name(start_row, start_col));
590 alphabetic_text_entry::alphabetic_text_entry(char *s, const entry_modifier *m)
591 : text_entry(s, m)
595 void alphabetic_text_entry::do_width()
597 printfs(".ds %1 \"", text_string_name(start_row, start_col));
598 set_inline_modifier(mod);
599 prints(contents);
600 restore_inline_modifier(mod);
601 prints('\n');
602 set_location();
603 printfs(".nr %1 \\n[%1]>?\\w'\\*[%2]'\n",
604 span_alphabetic_width_reg(start_col, end_col),
605 text_string_name(start_row, start_col));
608 void alphabetic_text_entry::simple_print(int)
610 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
611 printfs("\\h'\\n[%1]u-\\n[%2]u/2u'",
612 span_width_reg(start_col, end_col),
613 span_alphabetic_width_reg(start_col, end_col));
614 printfs("\\*[%1]", text_string_name(start_row, start_col));
617 // The only point of this is to make `\a' ``work'' as in Unix tbl. Grrr.
619 void alphabetic_text_entry::add_tab()
621 printfs(" \\n[%1]u", column_end_reg(end_col));
624 block_entry::block_entry(char *s, const entry_modifier *m)
625 : table_entry(m), contents(s)
629 block_entry::~block_entry()
631 a_delete contents;
634 void block_entry::position_vertically()
636 if (start_row != end_row)
637 switch(mod->vertical_alignment) {
638 case entry_modifier::TOP:
639 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
640 break;
641 case entry_modifier::CENTER:
642 // Peform the motion in two stages so that the center is rounded
643 // vertically upwards even if net vertical motion is upwards.
644 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
645 printfs(".sp \\n[" BOTTOM_REG "]u-\\n[%1]u-\\n[%2]u/2u\n",
646 row_start_reg(start_row),
647 block_height_reg(start_row, start_col));
648 break;
649 case entry_modifier::BOTTOM:
650 printfs(".sp |\\n[%1]u+\\n[" BOTTOM_REG "]u-\\n[%1]u-\\n[%2]u\n",
651 row_start_reg(start_row),
652 block_height_reg(start_row, start_col));
653 break;
654 default:
655 assert(0);
657 if (mod->stagger)
658 prints(".sp -.5v\n");
661 int block_entry::divert(int ncols, const string *mw)
663 do_divert(0, ncols, mw);
664 return 1;
667 void block_entry::do_divert(int alphabetic, int ncols, const string *mw)
669 printfs(".di %1\n", block_diversion_name(start_row, start_col));
670 prints(".if \\n[" SAVED_FILL_REG "] .fi\n"
671 ".in 0\n");
672 if (start_col == end_col && !mw[start_col].empty())
673 printfs(".ll (n;%1)>?\\n[%2]u",
674 mw[start_col],
675 span_width_reg(start_col, start_col));
676 else
677 printfs(".ll (u;\\n[%1]>?(\\n[.l]*%2/%3))",
678 span_width_reg(start_col, end_col),
679 as_string(end_col - start_col + 1),
680 as_string(ncols + 1));
681 if (alphabetic)
682 prints("-2n");
683 prints("\n");
684 set_modifier(mod);
685 prints(".cp \\n(" COMPATIBLE_REG "\n");
686 set_location();
687 prints(contents);
688 prints(".br\n.di\n.cp 0\n");
689 if (!mod->zero_width) {
690 if (alphabetic) {
691 printfs(".nr %1 \\n[%1]>?(\\n[dl]+2n)\n",
692 span_width_reg(start_col, end_col));
693 printfs(".nr %1 \\n[%1]>?\\n[dl]\n",
694 span_alphabetic_width_reg(start_col, end_col));
696 else
697 printfs(".nr %1 \\n[%1]>?\\n[dl]\n", span_width_reg(start_col, end_col));
699 printfs(".nr %1 \\n[dn]\n", block_height_reg(start_row, start_col));
700 printfs(".nr %1 \\n[dl]\n", block_width_reg(start_row, start_col));
701 prints("." RESET_MACRO_NAME "\n"
702 ".in \\n[" SAVED_INDENT_REG "]u\n"
703 ".nf\n");
704 // the block might have contained .lf commands
705 location_force_filename = 1;
708 void block_entry::do_width()
710 // do nothing; the action happens in divert
713 void block_entry::do_depth()
715 printfs(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?(\\n[%1]+\\n[%2])\n",
716 row_start_reg(start_row),
717 block_height_reg(start_row, start_col));
720 left_block_entry::left_block_entry(char *s, const entry_modifier *m)
721 : block_entry(s, m)
725 void left_block_entry::print()
727 printfs(".in +\\n[%1]u\n", column_start_reg(start_col));
728 printfs(".%1\n", block_diversion_name(start_row, start_col));
729 prints(".in\n");
734 right_block_entry::right_block_entry(char *s, const entry_modifier *m)
735 : block_entry(s, m)
739 void right_block_entry::print()
741 printfs(".in +\\n[%1]u+\\n[%2]u-\\n[%3]u\n",
742 column_start_reg(start_col),
743 span_width_reg(start_col, end_col),
744 block_width_reg(start_row, start_col));
745 printfs(".%1\n", block_diversion_name(start_row, start_col));
746 prints(".in\n");
749 center_block_entry::center_block_entry(char *s, const entry_modifier *m)
750 : block_entry(s, m)
754 void center_block_entry::print()
756 printfs(".in +\\n[%1]u+(\\n[%2]u-\\n[%3]u/2u)\n",
757 column_start_reg(start_col),
758 span_width_reg(start_col, end_col),
759 block_width_reg(start_row, start_col));
760 printfs(".%1\n", block_diversion_name(start_row, start_col));
761 prints(".in\n");
764 alphabetic_block_entry::alphabetic_block_entry(char *s,
765 const entry_modifier *m)
766 : block_entry(s, m)
770 int alphabetic_block_entry::divert(int ncols, const string *mw)
772 do_divert(1, ncols, mw);
773 return 1;
776 void alphabetic_block_entry::print()
778 printfs(".in +\\n[%1]u+(\\n[%2]u-\\n[%3]u/2u)\n",
779 column_start_reg(start_col),
780 span_width_reg(start_col, end_col),
781 span_alphabetic_width_reg(start_row, end_col));
782 printfs(".%1\n", block_diversion_name(start_row, start_col));
783 prints(".in\n");
786 line_entry::line_entry(const entry_modifier *m)
787 : simple_entry(m), double_vrule_on_right(0), double_vrule_on_left(0)
791 void line_entry::note_double_vrule_on_right(int is_corner)
793 double_vrule_on_right = is_corner ? 1 : 2;
796 void line_entry::note_double_vrule_on_left(int is_corner)
798 double_vrule_on_left = is_corner ? 1 : 2;
802 single_line_entry::single_line_entry(const entry_modifier *m)
803 : line_entry(m)
807 int single_line_entry::line_type()
809 return 1;
812 void single_line_entry::simple_print(int dont_move)
814 printfs("\\h'|\\n[%1]u",
815 column_divide_reg(start_col));
816 if (double_vrule_on_left) {
817 prints(double_vrule_on_left == 1 ? "-" : "+");
818 prints(HALF_DOUBLE_LINE_SEP);
820 prints("'");
821 if (!dont_move)
822 prints("\\v'-" BAR_HEIGHT "'");
823 printfs("\\s[\\n[" LINESIZE_REG "]]" "\\D'l |\\n[%1]u",
824 column_divide_reg(end_col+1));
825 if (double_vrule_on_right) {
826 prints(double_vrule_on_left == 1 ? "+" : "-");
827 prints(HALF_DOUBLE_LINE_SEP);
829 prints("0'\\s0");
830 if (!dont_move)
831 prints("\\v'" BAR_HEIGHT "'");
834 single_line_entry *single_line_entry::to_single_line_entry()
836 return this;
839 double_line_entry::double_line_entry(const entry_modifier *m)
840 : line_entry(m)
844 int double_line_entry::line_type()
846 return 2;
849 void double_line_entry::simple_print(int dont_move)
851 if (!dont_move)
852 prints("\\v'-" BAR_HEIGHT "'");
853 printfs("\\h'|\\n[%1]u",
854 column_divide_reg(start_col));
855 if (double_vrule_on_left) {
856 prints(double_vrule_on_left == 1 ? "-" : "+");
857 prints(HALF_DOUBLE_LINE_SEP);
859 prints("'");
860 printfs("\\v'-" HALF_DOUBLE_LINE_SEP "'"
861 "\\s[\\n[" LINESIZE_REG "]]"
862 "\\D'l |\\n[%1]u",
863 column_divide_reg(end_col+1));
864 if (double_vrule_on_right)
865 prints("-" HALF_DOUBLE_LINE_SEP);
866 prints(" 0'");
867 printfs("\\v'" DOUBLE_LINE_SEP "'"
868 "\\D'l |\\n[%1]u",
869 column_divide_reg(start_col));
870 if (double_vrule_on_right) {
871 prints(double_vrule_on_left == 1 ? "+" : "-");
872 prints(HALF_DOUBLE_LINE_SEP);
874 prints(" 0'");
875 prints("\\s0"
876 "\\v'-" HALF_DOUBLE_LINE_SEP "'");
877 if (!dont_move)
878 prints("\\v'" BAR_HEIGHT "'");
881 double_line_entry *double_line_entry::to_double_line_entry()
883 return this;
886 short_line_entry::short_line_entry(const entry_modifier *m)
887 : simple_entry(m)
891 int short_line_entry::line_type()
893 return 1;
896 void short_line_entry::simple_print(int dont_move)
898 if (mod->stagger)
899 prints("\\v'-.5v'");
900 if (!dont_move)
901 prints("\\v'-" BAR_HEIGHT "'");
902 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
903 printfs("\\s[\\n[" LINESIZE_REG "]]"
904 "\\D'l \\n[%1]u 0'"
905 "\\s0",
906 span_width_reg(start_col, end_col));
907 if (!dont_move)
908 prints("\\v'" BAR_HEIGHT "'");
909 if (mod->stagger)
910 prints("\\v'.5v'");
913 short_double_line_entry::short_double_line_entry(const entry_modifier *m)
914 : simple_entry(m)
918 int short_double_line_entry::line_type()
920 return 2;
923 void short_double_line_entry::simple_print(int dont_move)
925 if (mod->stagger)
926 prints("\\v'-.5v'");
927 if (!dont_move)
928 prints("\\v'-" BAR_HEIGHT "'");
929 printfs("\\h'|\\n[%2]u'"
930 "\\v'-" HALF_DOUBLE_LINE_SEP "'"
931 "\\s[\\n[" LINESIZE_REG "]]"
932 "\\D'l \\n[%1]u 0'"
933 "\\v'" DOUBLE_LINE_SEP "'"
934 "\\D'l |\\n[%2]u 0'"
935 "\\s0"
936 "\\v'-" HALF_DOUBLE_LINE_SEP "'",
937 span_width_reg(start_col, end_col),
938 column_start_reg(start_col));
939 if (!dont_move)
940 prints("\\v'" BAR_HEIGHT "'");
941 if (mod->stagger)
942 prints("\\v'.5v'");
945 void set_modifier(const entry_modifier *m)
947 if (!m->font.empty())
948 printfs(".ft %1\n", m->font);
949 if (m->point_size.val != 0) {
950 prints(".ps ");
951 if (m->point_size.inc > 0)
952 prints('+');
953 else if (m->point_size.inc < 0)
954 prints('-');
955 printfs("%1\n", as_string(m->point_size.val));
957 if (m->vertical_spacing.val != 0) {
958 prints(".vs ");
959 if (m->vertical_spacing.inc > 0)
960 prints('+');
961 else if (m->vertical_spacing.inc < 0)
962 prints('-');
963 printfs("%1\n", as_string(m->vertical_spacing.val));
967 void set_inline_modifier(const entry_modifier *m)
969 if (!m->font.empty())
970 printfs("\\f[%1]", m->font);
971 if (m->point_size.val != 0) {
972 prints("\\s[");
973 if (m->point_size.inc > 0)
974 prints('+');
975 else if (m->point_size.inc < 0)
976 prints('-');
977 printfs("%1]", as_string(m->point_size.val));
979 if (m->stagger)
980 prints("\\v'-.5v'");
983 void restore_inline_modifier(const entry_modifier *m)
985 if (!m->font.empty())
986 prints("\\f[\\n[" SAVED_FONT_REG "]]");
987 if (m->point_size.val != 0)
988 prints("\\s[\\n[" SAVED_SIZE_REG "]]");
989 if (m->stagger)
990 prints("\\v'.5v'");
994 struct stuff {
995 stuff *next;
996 int row; // occurs before row `row'
997 char printed; // has it been printed?
999 stuff(int);
1000 virtual void print(table *) = 0;
1001 virtual ~stuff();
1002 virtual int is_single_line() { return 0; };
1003 virtual int is_double_line() { return 0; };
1006 stuff::stuff(int r) : row(r), next(0), printed(0)
1010 stuff::~stuff()
1014 struct text_stuff : stuff {
1015 string contents;
1016 const char *filename;
1017 int lineno;
1019 text_stuff(const string &, int r, const char *fn, int ln);
1020 ~text_stuff();
1021 void print(table *);
1025 text_stuff::text_stuff(const string &s, int r, const char *fn, int ln)
1026 : contents(s), stuff(r), filename(fn), lineno(ln)
1030 text_stuff::~text_stuff()
1034 void text_stuff::print(table *)
1036 printed = 1;
1037 prints(".cp \\n(" COMPATIBLE_REG "\n");
1038 set_troff_location(filename, lineno);
1039 prints(contents);
1040 prints(".cp 0\n");
1041 location_force_filename = 1; // it might have been a .lf command
1044 struct single_hline_stuff : stuff {
1045 single_hline_stuff(int r);
1046 void print(table *);
1047 int is_single_line();
1050 single_hline_stuff::single_hline_stuff(int r) : stuff(r)
1054 void single_hline_stuff::print(table *tbl)
1056 printed = 1;
1057 tbl->print_single_hline(row);
1060 int single_hline_stuff::is_single_line()
1062 return 1;
1065 struct double_hline_stuff : stuff {
1066 double_hline_stuff(int r);
1067 void print(table *);
1068 int is_double_line();
1071 double_hline_stuff::double_hline_stuff(int r) : stuff(r)
1075 void double_hline_stuff::print(table *tbl)
1077 printed = 1;
1078 tbl->print_double_hline(row);
1081 int double_hline_stuff::is_double_line()
1083 return 1;
1086 struct vertical_rule {
1087 vertical_rule *next;
1088 short start_row;
1089 short end_row;
1090 short col;
1091 char is_double;
1092 string top_adjust;
1093 string bot_adjust;
1095 vertical_rule(int sr, int er, int c, int dbl, vertical_rule *);
1096 ~vertical_rule();
1097 void contribute_to_bottom_macro(table *);
1098 void print();
1101 vertical_rule::vertical_rule(int sr, int er, int c, int dbl, vertical_rule *p)
1102 : start_row(sr), end_row(er), col(c), is_double(dbl), next(p)
1106 vertical_rule::~vertical_rule()
1110 void vertical_rule::contribute_to_bottom_macro(table *tbl)
1112 printfs(".if \\n[" CURRENT_ROW_REG "]>=%1",
1113 as_string(start_row));
1114 if (end_row != tbl->get_nrows() - 1)
1115 printfs("&(\\n[" CURRENT_ROW_REG "]<%1)",
1116 as_string(end_row));
1117 prints(" \\{");
1118 printfs(".if %1<=\\n[" LAST_PASSED_ROW_REG "] .nr %2 \\n[#T]\n",
1119 as_string(start_row),
1120 row_top_reg(start_row));
1121 const char *offset_table[3];
1122 if (is_double) {
1123 offset_table[0] = "-" HALF_DOUBLE_LINE_SEP;
1124 offset_table[1] = "+" HALF_DOUBLE_LINE_SEP;
1125 offset_table[2] = 0;
1127 else {
1128 offset_table[0] = "";
1129 offset_table[1] = 0;
1131 for (const char **offsetp = offset_table; *offsetp; offsetp++) {
1132 prints(".sp -1\n"
1133 "\\v'" BODY_DEPTH);
1134 if (!bot_adjust.empty())
1135 printfs("+%1", bot_adjust);
1136 prints("'");
1137 printfs("\\h'\\n[%1]u%3'\\s[\\n[" LINESIZE_REG "]]\\D'l 0 |\\n[%2]u-1v",
1138 column_divide_reg(col),
1139 row_top_reg(start_row),
1140 *offsetp);
1141 if (!bot_adjust.empty())
1142 printfs("-(%1)", bot_adjust);
1143 // don't perform the top adjustment if the top is actually #T
1144 if (!top_adjust.empty())
1145 printfs("+((%1)*(%2>\\n[" LAST_PASSED_ROW_REG "]))",
1146 top_adjust,
1147 as_string(start_row));
1148 prints("'\\s0\n");
1150 prints(".\\}\n");
1153 void vertical_rule::print()
1155 printfs("\\*[" TRANSPARENT_STRING_NAME "]"
1156 ".if %1<=\\*[" QUOTE_STRING_NAME "]\\n[" LAST_PASSED_ROW_REG "] "
1157 ".nr %2 \\*[" QUOTE_STRING_NAME "]\\n[#T]\n",
1158 as_string(start_row),
1159 row_top_reg(start_row));
1160 const char *offset_table[3];
1161 if (is_double) {
1162 offset_table[0] = "-" HALF_DOUBLE_LINE_SEP;
1163 offset_table[1] = "+" HALF_DOUBLE_LINE_SEP;
1164 offset_table[2] = 0;
1166 else {
1167 offset_table[0] = "";
1168 offset_table[1] = 0;
1170 for (const char **offsetp = offset_table; *offsetp; offsetp++) {
1171 prints("\\*[" TRANSPARENT_STRING_NAME "].sp -1\n"
1172 "\\*[" TRANSPARENT_STRING_NAME "]\\v'" BODY_DEPTH);
1173 if (!bot_adjust.empty())
1174 printfs("+%1", bot_adjust);
1175 prints("'");
1176 printfs("\\h'\\n[%1]u%3'"
1177 "\\s[\\n[" LINESIZE_REG "]]"
1178 "\\D'l 0 |\\*[" QUOTE_STRING_NAME "]\\n[%2]u-1v",
1179 column_divide_reg(col),
1180 row_top_reg(start_row),
1181 *offsetp);
1182 if (!bot_adjust.empty())
1183 printfs("-(%1)", bot_adjust);
1184 // don't perform the top adjustment if the top is actually #T
1185 if (!top_adjust.empty())
1186 printfs("+((%1)*(%2>\\*[" QUOTE_STRING_NAME "]\\n["
1187 LAST_PASSED_ROW_REG "]))",
1188 top_adjust,
1189 as_string(start_row));
1190 prints("'"
1191 "\\s0\n");
1195 table::table(int nc, unsigned f, int ls)
1196 : ncolumns(nc), flags(f), linesize(ls),
1197 nrows(0), allocated_rows(0), entry(0), entry_list(0),
1198 left_separation(0), right_separation(0), stuff_list(0), vline(0),
1199 vrule_list(0), row_is_all_lines(0), span_list(0)
1201 minimum_width = new string[ncolumns];
1202 column_separation = new int[ncolumns - 1];
1203 equal = new char[ncolumns];
1204 int i;
1205 for (i = 0; i < ncolumns; i++)
1206 equal[i] = 0;
1207 for (i = 0; i < ncolumns-1; i++)
1208 column_separation[i] = DEFAULT_COLUMN_SEPARATION;
1209 delim[0] = delim[1] = '\0';
1212 table::~table()
1214 for (int i = 0; i < nrows; i++) {
1215 a_delete entry[i];
1216 a_delete vline[i];
1218 a_delete entry;
1219 a_delete vline;
1220 while (entry_list) {
1221 table_entry *tem = entry_list;
1222 entry_list = entry_list->next;
1223 delete tem;
1225 ad_delete(ncolumns) minimum_width;
1226 a_delete column_separation;
1227 a_delete equal;
1228 while (stuff_list) {
1229 stuff *tem = stuff_list;
1230 stuff_list = stuff_list->next;
1231 delete tem;
1233 while (vrule_list) {
1234 vertical_rule *tem = vrule_list;
1235 vrule_list = vrule_list->next;
1236 delete tem;
1238 a_delete row_is_all_lines;
1239 while (span_list) {
1240 horizontal_span *tem = span_list;
1241 span_list = span_list->next;
1242 delete tem;
1246 void table::set_delim(char c1, char c2)
1248 delim[0] = c1;
1249 delim[1] = c2;
1252 void table::set_minimum_width(int c, const string &w)
1254 assert(c >= 0 && c < ncolumns);
1255 minimum_width[c] = w;
1258 void table::set_column_separation(int c, int n)
1260 assert(c >= 0 && c < ncolumns - 1);
1261 column_separation[c] = n;
1264 void table::set_equal_column(int c)
1266 assert(c >= 0 && c < ncolumns);
1267 equal[c] = 1;
1270 void table::add_stuff(stuff *p)
1272 for (stuff **pp = &stuff_list; *pp; pp = &(*pp)->next)
1274 *pp = p;
1277 void table::add_text_line(int r, const string &s, const char *filename, int lineno)
1279 add_stuff(new text_stuff(s, r, filename, lineno));
1282 void table::add_single_hline(int r)
1284 add_stuff(new single_hline_stuff(r));
1287 void table::add_double_hline(int r)
1289 add_stuff(new double_hline_stuff(r));
1292 void table::allocate(int r)
1294 if (r >= nrows) {
1295 typedef table_entry **PPtable_entry; // work around g++ 1.36.1 bug
1296 if (r >= allocated_rows) {
1297 if (allocated_rows == 0) {
1298 allocated_rows = 16;
1299 if (allocated_rows <= r)
1300 allocated_rows = r + 1;
1301 entry = new PPtable_entry[allocated_rows];
1302 vline = new char*[allocated_rows];
1304 else {
1305 table_entry ***old_entry = entry;
1306 int old_allocated_rows = allocated_rows;
1307 allocated_rows *= 2;
1308 if (allocated_rows <= r)
1309 allocated_rows = r + 1;
1310 entry = new PPtable_entry[allocated_rows];
1311 memcpy(entry, old_entry, sizeof(table_entry**)*old_allocated_rows);
1312 a_delete old_entry;
1313 char **old_vline = vline;
1314 vline = new char*[allocated_rows];
1315 memcpy(vline, old_vline, sizeof(char*)*old_allocated_rows);
1316 a_delete old_vline;
1319 assert(allocated_rows > r);
1320 while (nrows <= r) {
1321 entry[nrows] = new table_entry*[ncolumns];
1322 for (int i = 0; i < ncolumns; i++)
1323 entry[nrows][i] = 0;
1324 vline[nrows] = new char[ncolumns+1];
1325 for (i = 0; i < ncolumns+1; i++)
1326 vline[nrows][i] = 0;
1327 nrows++;
1332 void table::do_hspan(int r, int c)
1334 assert(r >= 0 && c >= 0 && r < nrows && c < ncolumns);
1335 if (c == 0) {
1336 error("first column cannot be horizontally spanned");
1337 return;
1339 table_entry *e = entry[r][c];
1340 if (e) {
1341 assert(e->start_row <= r && r <= e->end_row
1342 && e->start_col <= c && c <= e->end_col
1343 && e->end_row - e->start_row > 0
1344 && e->end_col - e->start_col > 0);
1345 return;
1347 e = entry[r][c-1];
1348 // e can be 0 if we had an empty entry or an error
1349 if (e == 0)
1350 return;
1351 if (e->start_row != r) {
1354 ^ s */
1355 error("impossible horizontal span at row %1, column %2", r + 1, c + 1);
1357 else {
1358 e->end_col = c;
1359 entry[r][c] = e;
1363 void table::do_vspan(int r, int c)
1365 assert(r >= 0 && c >= 0 && r < nrows && c < ncolumns);
1366 if (r == 0) {
1367 error("first row cannot be vertically spanned");
1368 return;
1370 table_entry *e = entry[r][c];
1371 if (e) {
1372 assert(e->start_row <= r && r <= e->end_row
1373 && e->start_col <= c && c <= e->end_col
1374 && e->end_row - e->start_row > 0
1375 && e->end_col - e->start_col > 0);
1376 return;
1378 e = entry[r-1][c];
1379 // e can be 0 if we had an empty entry or an error
1380 if (e == 0)
1381 return;
1382 if (e->start_col != c) {
1383 /* l s
1384 l ^ */
1385 error("impossible vertical span at row %1, column %2", r + 1, c + 1);
1387 else {
1388 for (int i = c; i <= e->end_col; i++) {
1389 assert(entry[r][i] == 0);
1390 entry[r][i] = e;
1392 e->end_row = r;
1396 int find_dot(const char *s, const char *delim)
1398 if (s == 0 || *s == '\0')
1399 return -1;
1400 const char *p;
1401 int in_delim = 0; // is p within eqn delimiters?
1402 // tbl recognises \& even within eqn delimiters; I don't
1403 for (p = s; *p; p++)
1404 if (in_delim) {
1405 if (*p == delim[1])
1406 in_delim = 0;
1408 else if (*p == delim[0])
1409 in_delim = 1;
1410 else if (p[0] == '\\' && p[1] == '&')
1411 return p - s;
1412 int possible_pos = -1;
1413 in_delim = 0;
1414 for (p = s; *p; p++)
1415 if (in_delim) {
1416 if (*p == delim[1])
1417 in_delim = 0;
1419 else if (*p == delim[0])
1420 in_delim = 1;
1421 else if (p[0] == '.' && csdigit(p[1]))
1422 possible_pos = p - s;
1423 if (possible_pos >= 0)
1424 return possible_pos;
1425 in_delim = 0;
1426 for (p = s; *p; p++)
1427 if (in_delim) {
1428 if (*p == delim[1])
1429 in_delim = 0;
1431 else if (*p == delim[0])
1432 in_delim = 1;
1433 else if (csdigit(*p))
1434 possible_pos = p + 1 - s;
1435 return possible_pos;
1438 void table::add_entry(int r, int c, const string &str, const entry_format *f,
1439 const char *fn, int ln)
1441 allocate(r);
1442 table_entry *e = 0;
1443 if (str == "\\_") {
1444 e = new short_line_entry(f);
1446 else if (str == "\\=") {
1447 e = new short_double_line_entry(f);
1449 else if (str == "_") {
1450 single_line_entry *lefte;
1451 if (c > 0 && entry[r][c-1] != 0 &&
1452 (lefte = entry[r][c-1]->to_single_line_entry()) != 0
1453 && lefte->start_row == r
1454 && lefte->mod->stagger == f->stagger) {
1455 lefte->end_col = c;
1456 entry[r][c] = lefte;
1458 else
1459 e = new single_line_entry(f);
1461 else if (str == "=") {
1462 double_line_entry *lefte;
1463 if (c > 0 && entry[r][c-1] != 0 &&
1464 (lefte = entry[r][c-1]->to_double_line_entry()) != 0
1465 && lefte->start_row == r
1466 && lefte->mod->stagger == f->stagger) {
1467 lefte->end_col = c;
1468 entry[r][c] = lefte;
1470 else
1471 e = new double_line_entry(f);
1473 else if (str == "\\^") {
1474 do_vspan(r, c);
1476 else if (str.length() > 2 && str[0] == '\\' && str[1] == 'R') {
1477 if (str.search('\n') >= 0)
1478 error_with_file_and_line(fn, ln, "bad repeated character");
1479 else {
1480 char *s = str.substring(2, str.length() - 2).extract();
1481 e = new repeated_char_entry(s, f);
1484 else {
1485 int is_block = str.search('\n') >= 0;
1486 char *s;
1487 switch (f->type) {
1488 case FORMAT_SPAN:
1489 assert(str.empty());
1490 do_hspan(r, c);
1491 break;
1492 case FORMAT_LEFT:
1493 if (!str.empty()) {
1494 s = str.extract();
1495 if (is_block)
1496 e = new left_block_entry(s, f);
1497 else
1498 e = new left_text_entry(s, f);
1500 else
1501 e = new empty_entry(f);
1502 break;
1503 case FORMAT_CENTER:
1504 if (!str.empty()) {
1505 s = str.extract();
1506 if (is_block)
1507 e = new center_block_entry(s, f);
1508 else
1509 e = new center_text_entry(s, f);
1511 else
1512 e = new empty_entry(f);
1513 break;
1514 case FORMAT_RIGHT:
1515 if (!str.empty()) {
1516 s = str.extract();
1517 if (is_block)
1518 e = new right_block_entry(s, f);
1519 else
1520 e = new right_text_entry(s, f);
1522 else
1523 e = new empty_entry(f);
1524 break;
1525 case FORMAT_NUMERIC:
1526 if (!str.empty()) {
1527 s = str.extract();
1528 if (is_block) {
1529 error_with_file_and_line(fn, ln, "can't have numeric text block");
1530 e = new left_block_entry(s, f);
1532 else {
1533 int pos = find_dot(s, delim);
1534 if (pos < 0)
1535 e = new center_text_entry(s, f);
1536 else
1537 e = new numeric_text_entry(s, f, pos);
1540 else
1541 e = new empty_entry(f);
1542 break;
1543 case FORMAT_ALPHABETIC:
1544 if (!str.empty()) {
1545 s = str.extract();
1546 if (is_block)
1547 e = new alphabetic_block_entry(s, f);
1548 else
1549 e = new alphabetic_text_entry(s, f);
1551 else
1552 e = new empty_entry(f);
1553 break;
1554 case FORMAT_VSPAN:
1555 do_vspan(r, c);
1556 break;
1557 case FORMAT_HLINE:
1558 if (str.length() != 0)
1559 error_with_file_and_line(fn, ln,
1560 "non-empty data entry for `_' format ignored");
1561 e = new single_line_entry(f);
1562 break;
1563 case FORMAT_DOUBLE_HLINE:
1564 if (str.length() != 0)
1565 error_with_file_and_line(fn, ln,
1566 "non-empty data entry for `=' format ignored");
1567 e = new double_line_entry(f);
1568 break;
1569 default:
1570 assert(0);
1573 if (e) {
1574 table_entry *preve = entry[r][c];
1575 if (preve) {
1576 /* c s
1577 ^ l */
1578 error_with_file_and_line(fn, ln, "row %1, column %2 already spanned",
1579 r + 1, c + 1);
1580 delete e;
1582 else {
1583 e->input_lineno = ln;
1584 e->input_filename = fn;
1585 e->start_row = e->end_row = r;
1586 e->start_col = e->end_col = c;
1587 for (table_entry **p = &entry_list; *p; p = &(*p)->next)
1589 *p = e;
1590 entry[r][c] = e;
1595 // add vertical lines for row r
1597 void table::add_vlines(int r, const char *v)
1599 allocate(r);
1600 for (int i = 0; i < ncolumns+1; i++)
1601 vline[r][i] = v[i];
1604 void table::check()
1606 table_entry *p = entry_list;
1607 int i, j;
1608 while (p) {
1609 for (i = p->start_row; i <= p->end_row; i++)
1610 for (j = p->start_col; j <= p->end_col; j++)
1611 assert(entry[i][j] == p);
1612 p = p->next;
1616 void table::print()
1618 location_force_filename = 1;
1619 check();
1620 init_output();
1621 determine_row_type();
1622 compute_widths();
1623 if (!(flags & CENTER))
1624 prints(".if \\n[" SAVED_CENTER_REG "] \\{");
1625 prints(".in +(u;\\n[.l]-\\n[.i]-\\n[TW]/2)\n"
1626 ".nr " SAVED_INDENT_REG " \\n[.i]\n");
1627 if (!(flags & CENTER))
1628 prints(".\\}\n");
1629 build_vrule_list();
1630 define_bottom_macro();
1631 do_top();
1632 for (int i = 0; i < nrows; i++)
1633 do_row(i);
1634 do_bottom();
1637 void table::determine_row_type()
1639 row_is_all_lines = new char[nrows];
1640 for (int i = 0; i < nrows; i++) {
1641 int had_single = 0;
1642 int had_double = 0;
1643 int had_non_line = 0;
1644 for (int c = 0; c < ncolumns; c++) {
1645 table_entry *e = entry[i][c];
1646 if (e != 0) {
1647 if (e->start_row == e->end_row) {
1648 int t = e->line_type();
1649 switch (t) {
1650 case -1:
1651 had_non_line = 1;
1652 break;
1653 case 0:
1654 // empty
1655 break;
1656 case 1:
1657 had_single = 1;
1658 break;
1659 case 2:
1660 had_double = 1;
1661 break;
1662 default:
1663 assert(0);
1665 if (had_non_line)
1666 break;
1668 c = e->end_col;
1671 if (had_non_line)
1672 row_is_all_lines[i] = 0;
1673 else if (had_double)
1674 row_is_all_lines[i] = 2;
1675 else if (had_single)
1676 row_is_all_lines[i] = 1;
1677 else
1678 row_is_all_lines[i] = 0;
1683 void table::init_output()
1685 prints(".nr " COMPATIBLE_REG " \\n(.C\n"
1686 ".cp 0\n");
1687 if (linesize > 0)
1688 printfs(".nr " LINESIZE_REG " %1\n", as_string(linesize));
1689 else
1690 prints(".nr " LINESIZE_REG " \\n[.s]\n");
1691 if (!(flags & CENTER))
1692 prints(".nr " SAVED_CENTER_REG " \\n[.ce]\n");
1693 prints(".de " RESET_MACRO_NAME "\n"
1694 ".ft \\n[.f]\n"
1695 ".ps \\n[.s]\n"
1696 ".vs \\n[.v]u\n"
1697 ".in \\n[.i]u\n"
1698 ".ll \\n[.l]u\n"
1699 ".ls \\n[.L]\n"
1700 ".ad \\n[.j]\n"
1701 ".ie \\n[.u] .fi\n"
1702 ".el .nf\n"
1703 ".ce \\n[.ce]\n"
1704 "..\n"
1705 ".nr " SAVED_INDENT_REG " \\n[.i]\n"
1706 ".nr " SAVED_FONT_REG " \\n[.f]\n"
1707 ".nr " SAVED_SIZE_REG " \\n[.s]\n"
1708 ".nr " SAVED_FILL_REG " \\n[.u]\n"
1709 ".nr T. 0\n"
1710 ".nr " CURRENT_ROW_REG " 0-1\n"
1711 ".nr " LAST_PASSED_ROW_REG " 0-1\n"
1712 ".nr " SECTION_DIVERSION_FLAG_REG " 0\n"
1713 ".ds " TRANSPARENT_STRING_NAME "\n"
1714 ".ds " QUOTE_STRING_NAME "\n"
1715 ".nr " NEED_BOTTOM_RULE_REG " 1\n"
1716 ".nr " SUPPRESS_BOTTOM_REG " 0\n"
1717 ".eo\n"
1718 ".de " KEEP_MACRO_NAME "\n"
1719 ".if '\\n[.z]'' \\{.ds " QUOTE_STRING_NAME " \\\\\n"
1720 ".ds " TRANSPARENT_STRING_NAME " \\!\n"
1721 ".di " SECTION_DIVERSION_NAME "\n"
1722 ".nr " SECTION_DIVERSION_FLAG_REG " 1\n"
1723 ".in 0\n"
1724 ".\\}\n"
1725 "..\n"
1726 ".de " RELEASE_MACRO_NAME "\n"
1727 ".if \\n[" SECTION_DIVERSION_FLAG_REG "] \\{"
1728 ".di\n"
1729 ".in \\n[" SAVED_INDENT_REG "]u\n"
1730 ".nr " SAVED_DN_REG " \\n[dn]\n"
1731 ".ds " QUOTE_STRING_NAME "\n"
1732 ".ds " TRANSPARENT_STRING_NAME "\n"
1733 ".nr " SECTION_DIVERSION_FLAG_REG " 0\n"
1734 ".if \\n[.t]<=\\n[dn] \\{"
1735 ".nr T. 1\n"
1736 ".T#\n"
1737 ".nr " SUPPRESS_BOTTOM_REG " 1\n"
1738 ".sp \\n[.t]u\n"
1739 ".nr " SUPPRESS_BOTTOM_REG " 0\n"
1740 ".mk #T\n"
1741 ".\\}\n"
1742 ".if \\n[.t]<=\\n[" SAVED_DN_REG "] "
1743 /* Since we turn off traps, it won't get into an infinite loop
1744 when we try and print it; it will just go off the bottom of the
1745 page. */
1746 ".tm warning: page \\n%: table text block will not fit on one page\n"
1747 ".nf\n"
1748 ".ls 1\n"
1749 "." SECTION_DIVERSION_NAME "\n"
1750 ".ls\n"
1751 ".rm " SECTION_DIVERSION_NAME "\n"
1752 ".\\}\n"
1753 "..\n"
1754 ".nr " TABLE_DIVERSION_FLAG_REG " 0\n"
1755 ".de " TABLE_KEEP_MACRO_NAME "\n"
1756 ".if '\\n[.z]'' \\{"
1757 ".di " TABLE_DIVERSION_NAME "\n"
1758 ".nr " TABLE_DIVERSION_FLAG_REG " 1\n"
1759 ".\\}\n"
1760 "..\n"
1761 ".de " TABLE_RELEASE_MACRO_NAME "\n"
1762 ".if \\n[" TABLE_DIVERSION_FLAG_REG "] \\{.br\n"
1763 ".di\n"
1764 ".nr " SAVED_DN_REG " \\n[dn]\n"
1765 ".ne \\n[dn]u+\\n[.V]u\n"
1766 ".ie \\n[.t]<=\\n[" SAVED_DN_REG "] "
1767 ".tm error: page \\n%: table will not fit on one page; use .TS H/.TH\n"
1768 ".el \\{"
1769 ".in 0\n"
1770 ".ls 1\n"
1771 ".nf\n"
1772 "." TABLE_DIVERSION_NAME "\n"
1773 ".\\}\n"
1774 ".rm " TABLE_DIVERSION_NAME "\n"
1775 ".\\}\n"
1776 "..\n"
1777 ".de " REPEATED_MARK_MACRO "\n"
1778 ".mk \\$1\n"
1779 ".if !'\\n(.z'' \\!." REPEATED_MARK_MACRO " \"\\$1\"\n"
1780 "..\n"
1781 ".de " REPEATED_VPT_MACRO "\n"
1782 ".vpt \\$1\n"
1783 ".if !'\\n(.z'' \\!." REPEATED_VPT_MACRO " \"\\$1\"\n"
1784 "..\n"
1785 ".ec\n"
1786 ".ce 0\n"
1787 ".nf\n");
1790 string block_width_reg(int r, int c)
1792 static char name[sizeof(BLOCK_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1793 sprintf(name, BLOCK_WIDTH_PREFIX "%d,%d", r, c);
1794 return string(name);
1797 string block_diversion_name(int r, int c)
1799 static char name[sizeof(BLOCK_DIVERSION_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1800 sprintf(name, BLOCK_DIVERSION_PREFIX "%d,%d", r, c);
1801 return string(name);
1804 string text_string_name(int r, int c)
1806 static char name[sizeof(TEXT_STRING_PREFIX) + INT_DIGITS + 1 + INT_DIGITS];
1807 sprintf(name, TEXT_STRING_PREFIX "%d,%d", r, c);
1808 return string(name);
1811 string right_text_string_name(int r, int c)
1813 static char name[sizeof(RIGHT_TEXT_STRING_PREFIX)+INT_DIGITS+ 1+INT_DIGITS];
1814 sprintf(name, RIGHT_TEXT_STRING_PREFIX "%d,%d", r, c);
1815 return string(name);
1818 string block_height_reg(int r, int c)
1820 static char name[sizeof(BLOCK_HEIGHT_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1821 sprintf(name, BLOCK_HEIGHT_PREFIX "%d,%d", r, c);
1822 return string(name);
1825 string span_width_reg(int start_col, int end_col)
1827 static char name[sizeof(SPAN_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1828 sprintf(name, SPAN_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_left_numeric_width_reg(int start_col, int end_col)
1836 static char name[sizeof(SPAN_LEFT_NUMERIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1837 sprintf(name, SPAN_LEFT_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_right_numeric_width_reg(int start_col, int end_col)
1845 static char name[sizeof(SPAN_RIGHT_NUMERIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1846 sprintf(name, SPAN_RIGHT_NUMERIC_WIDTH_PREFIX "%d", start_col);
1847 if (end_col != start_col)
1848 sprintf(strchr(name, '\0'), ",%d", end_col);
1849 return string(name);
1852 string span_alphabetic_width_reg(int start_col, int end_col)
1854 static char name[sizeof(SPAN_ALPHABETIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1855 sprintf(name, SPAN_ALPHABETIC_WIDTH_PREFIX "%d", start_col);
1856 if (end_col != start_col)
1857 sprintf(strchr(name, '\0'), ",%d", end_col);
1858 return string(name);
1862 string column_separation_reg(int col)
1864 static char name[sizeof(COLUMN_SEPARATION_PREFIX)+INT_DIGITS];
1865 sprintf(name, COLUMN_SEPARATION_PREFIX "%d", col);
1866 return string(name);
1869 string row_start_reg(int row)
1871 static char name[sizeof(ROW_START_PREFIX)+INT_DIGITS];
1872 sprintf(name, ROW_START_PREFIX "%d", row);
1873 return string(name);
1876 string column_start_reg(int col)
1878 static char name[sizeof(COLUMN_START_PREFIX)+INT_DIGITS];
1879 sprintf(name, COLUMN_START_PREFIX "%d", col);
1880 return string(name);
1883 string column_end_reg(int col)
1885 static char name[sizeof(COLUMN_END_PREFIX)+INT_DIGITS];
1886 sprintf(name, COLUMN_END_PREFIX "%d", col);
1887 return string(name);
1890 string column_divide_reg(int col)
1892 static char name[sizeof(COLUMN_DIVIDE_PREFIX)+INT_DIGITS];
1893 sprintf(name, COLUMN_DIVIDE_PREFIX "%d", col);
1894 return string(name);
1897 string row_top_reg(int row)
1899 static char name[sizeof(ROW_TOP_PREFIX)+INT_DIGITS];
1900 sprintf(name, ROW_TOP_PREFIX "%d", row);
1901 return string(name);
1904 void init_span_reg(int start_col, int end_col)
1906 printfs(".nr %1 \\n(.H\n.nr %2 0\n.nr %3 0\n.nr %4 0\n",
1907 span_width_reg(start_col, end_col),
1908 span_alphabetic_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));
1913 void compute_span_width(int start_col, int end_col)
1915 printfs(".nr %1 \\n[%1]>?(\\n[%2]+\\n[%3])\n"
1916 ".if \\n[%4] .nr %1 \\n[%1]>?(\\n[%4]+2n)\n",
1917 span_width_reg(start_col, end_col),
1918 span_left_numeric_width_reg(start_col, end_col),
1919 span_right_numeric_width_reg(start_col, end_col),
1920 span_alphabetic_width_reg(start_col, end_col));
1924 // Increase the widths of columns so that the width of any spanning entry
1925 // is no greater than the sum of the widths of the columns that it spans.
1926 // Ensure that the widths of columns remain equal.
1928 void table::divide_span(int start_col, int end_col)
1930 assert(end_col > start_col);
1931 printfs(".nr " NEEDED_REG " \\n[%1]-(\\n[%2]",
1932 span_width_reg(start_col, end_col),
1933 span_width_reg(start_col, start_col));
1934 for (int i = start_col + 1; i <= end_col; i++) {
1935 // The column separation may shrink with the expand option.
1936 if (!(flags & EXPAND))
1937 printfs("+%1n", as_string(column_separation[i - 1]));
1938 printfs("+\\n[%1]", span_width_reg(i, i));
1940 prints(")\n");
1941 printfs(".nr " NEEDED_REG " \\n[" NEEDED_REG "]/%1\n",
1942 as_string(end_col - start_col + 1));
1943 prints(".if \\n[" NEEDED_REG "] \\{");
1944 for (i = start_col; i <= end_col; i++)
1945 printfs(".nr %1 +\\n[" NEEDED_REG "]\n",
1946 span_width_reg(i, i));
1947 int equal_flag = 0;
1948 for (i = start_col; i <= end_col && !equal_flag; i++)
1949 if (equal[i])
1950 equal_flag = 1;
1951 if (equal_flag) {
1952 for (i = 0; i < ncolumns; i++)
1953 if (i < start_col || i > end_col)
1954 printfs(".nr %1 +\\n[" NEEDED_REG "]\n",
1955 span_width_reg(i, i));
1957 prints(".\\}\n");
1961 void table::sum_columns(int start_col, int end_col)
1963 assert(end_col > start_col);
1964 printfs(".nr %1 \\n[%2]",
1965 span_width_reg(start_col, end_col),
1966 span_width_reg(start_col, start_col));
1967 for (int i = start_col + 1; i <= end_col; i++)
1968 printfs("+(%1*\\n[" SEPARATION_FACTOR_REG "])+\\n[%2]",
1969 as_string(column_separation[i - 1]),
1970 span_width_reg(i, i));
1971 prints('\n');
1974 horizontal_span::horizontal_span(int sc, int ec, horizontal_span *p)
1975 : start_col(sc), end_col(ec), next(p)
1979 void table::build_span_list()
1981 span_list = 0;
1982 table_entry *p = entry_list;
1983 while (p) {
1984 if (p->end_col != p->start_col) {
1985 for (horizontal_span *q = span_list; q; q = q->next)
1986 if (q->start_col == p->start_col
1987 && q->end_col == p->end_col)
1988 break;
1989 if (!q)
1990 span_list = new horizontal_span(p->start_col, p->end_col, span_list);
1992 p = p->next;
1994 // Now sort span_list primarily by order of end_row, and secondarily
1995 // by reverse order of start_row. This ensures that if we divide
1996 // spans using the order in span_list, we will get reasonable results.
1997 horizontal_span *unsorted = span_list;
1998 span_list = 0;
1999 while (unsorted) {
2000 for (horizontal_span **pp = &span_list; *pp; pp = &(*pp)->next)
2001 if (unsorted->end_col < (*pp)->end_col
2002 || (unsorted->end_col == (*pp)->end_col
2003 && (unsorted->start_col > (*pp)->start_col)))
2004 break;
2005 horizontal_span *tem = unsorted->next;
2006 unsorted->next = *pp;
2007 *pp = unsorted;
2008 unsorted = tem;
2013 void table::compute_separation_factor()
2015 if (flags & (ALLBOX|BOX|DOUBLEBOX))
2016 left_separation = right_separation = 1;
2017 else {
2018 for (int i = 0; i < nrows; i++) {
2019 if (vline[i][0] > 0)
2020 left_separation = 1;
2021 if (vline[i][ncolumns] > 0)
2022 right_separation = 1;
2025 if (flags & EXPAND) {
2026 int total_sep = left_separation + right_separation;
2027 for (int i = 0; i < ncolumns - 1; i++)
2028 total_sep += column_separation[i];
2029 if (total_sep != 0) {
2030 // Don't let the separation factor be negative.
2031 prints(".nr " SEPARATION_FACTOR_REG " \\n[.l]-\\n[.i]");
2032 for (i = 0; i < ncolumns; i++)
2033 printfs("-\\n[%1]", span_width_reg(i, i));
2034 printfs("/%1>?0\n", as_string(total_sep));
2039 void table::compute_column_positions()
2041 printfs(".nr %1 0\n", column_divide_reg(0));
2042 printfs(".nr %1 %2*\\n[" SEPARATION_FACTOR_REG "]\n",
2043 column_start_reg(0),
2044 as_string(left_separation));
2045 for (int i = 1;; i++) {
2046 printfs(".nr %1 \\n[%2]+\\n[%3]\n",
2047 column_end_reg(i-1),
2048 column_start_reg(i-1),
2049 span_width_reg(i-1, i-1));
2050 if (i >= ncolumns)
2051 break;
2052 printfs(".nr %1 \\n[%2]+(%3*\\n[" SEPARATION_FACTOR_REG "])\n",
2053 column_start_reg(i),
2054 column_end_reg(i-1),
2055 as_string(column_separation[i-1]));
2056 printfs(".nr %1 \\n[%2]+\\n[%3]/2\n",
2057 column_divide_reg(i),
2058 column_end_reg(i-1),
2059 column_start_reg(i));
2061 printfs(".nr %1 \\n[%2]+(%3*\\n[" SEPARATION_FACTOR_REG "])\n",
2062 column_divide_reg(ncolumns),
2063 column_end_reg(i-1),
2064 as_string(right_separation));
2065 printfs(".nr TW \\n[%1]\n",
2066 column_divide_reg(ncolumns));
2067 if (flags & DOUBLEBOX) {
2068 printfs(".nr %1 +" DOUBLE_LINE_SEP "\n", column_divide_reg(0));
2069 printfs(".nr %1 -" DOUBLE_LINE_SEP "\n", column_divide_reg(ncolumns));
2073 void table::make_columns_equal()
2075 int first = -1; // index of first equal column
2076 for (int i = 0; i < ncolumns; i++)
2077 if (equal[i]) {
2078 if (first < 0) {
2079 printfs(".nr %1 \\n[%1]", span_width_reg(i, i));
2080 first = i;
2082 else
2083 printfs(">?\\n[%1]", span_width_reg(i, i));
2085 if (first >= 0) {
2086 prints('\n');
2087 for (i = first + 1; i < ncolumns; i++)
2088 if (equal[i])
2089 printfs(".nr %1 \\n[%2]\n",
2090 span_width_reg(i, i),
2091 span_width_reg(first, first));
2095 void table::compute_widths()
2097 build_span_list();
2098 int i;
2099 horizontal_span *p;
2100 prints(".nr " SEPARATION_FACTOR_REG " 1n\n");
2101 for (i = 0; i < ncolumns; i++) {
2102 init_span_reg(i, i);
2103 if (!minimum_width[i].empty())
2104 printfs(".nr %1 %2\n", span_width_reg(i, i), minimum_width[i]);
2106 for (p = span_list; p; p = p->next)
2107 init_span_reg(p->start_col, p->end_col);
2108 table_entry *q;
2109 for (q = entry_list; q; q = q->next)
2110 if (!q->mod->zero_width)
2111 q->do_width();
2112 for (i = 0; i < ncolumns; i++)
2113 compute_span_width(i, i);
2114 for (p = span_list; p; p = p->next)
2115 compute_span_width(p->start_col, p->end_col);
2116 make_columns_equal();
2117 // Note that divide_span keeps equal width columns equal.
2118 for (p = span_list; p; p = p->next)
2119 divide_span(p->start_col, p->end_col);
2120 for (p = span_list; p; p = p->next)
2121 sum_columns(p->start_col, p->end_col);
2122 int had_spanning_block = 0;
2123 int had_equal_block = 0;
2124 for (q = entry_list; q; q = q->next)
2125 if (q->divert(ncolumns, minimum_width)) {
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 \\n[.v]u-" BODY_HEIGHT "-" BODY_DEPTH ">?\\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 for (int end_col = start_col;
2159 end_col < ncolumns
2160 && (entry[r][end_col] == 0
2161 || entry[r][end_col]->start_row == r);
2162 end_col++)
2164 if (end_col <= start_col)
2165 break;
2166 printfs("\\h'|\\n[%1]u",
2167 column_divide_reg(start_col));
2168 if ((r > 0 && vline[r-1][start_col] == 2)
2169 || (r < nrows && vline[r][start_col] == 2))
2170 prints("-" HALF_DOUBLE_LINE_SEP);
2171 prints("'");
2172 printfs("\\D'l |\\n[%1]u",
2173 column_divide_reg(end_col));
2174 if ((r > 0 && vline[r-1][end_col] == 2)
2175 || (r < nrows && vline[r][end_col] == 2))
2176 prints("+" HALF_DOUBLE_LINE_SEP);
2177 prints(" 0'");
2178 start_col = end_col;
2181 prints("\\s0\n");
2182 prints(".ls\n"
2183 ".vs\n");
2186 void table::print_double_hline(int r)
2188 prints(".vs \\n[.v]u-" BODY_HEIGHT "-" BODY_DEPTH "+" DOUBLE_LINE_SEP
2189 ">?\\n[.V]u\n"
2190 ".ls 1\n"
2191 "\\v'" BODY_DEPTH "'"
2192 "\\s[\\n[" LINESIZE_REG "]]");
2193 if (r > nrows - 1)
2194 prints("\\v'-" DOUBLE_LINE_SEP "'"
2195 "\\D'l |\\n[TW]u 0'"
2196 "\\v'" DOUBLE_LINE_SEP "'"
2197 "\\h'|0'"
2198 "\\D'l |\\n[TW]u 0'"
2199 "\n");
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 for (int end_col = start_col;
2208 end_col < ncolumns
2209 && (entry[r][end_col] == 0
2210 || entry[r][end_col]->start_row == r);
2211 end_col++)
2213 if (end_col <= start_col)
2214 break;
2215 const char *left_adjust = 0;
2216 if ((r > 0 && vline[r-1][start_col] == 2)
2217 || (r < nrows && vline[r][start_col] == 2))
2218 left_adjust = "-" HALF_DOUBLE_LINE_SEP;
2219 const char *right_adjust = 0;
2220 if ((r > 0 && vline[r-1][end_col] == 2)
2221 || (r < nrows && vline[r][end_col] == 2))
2222 right_adjust = "+" HALF_DOUBLE_LINE_SEP;
2223 printfs("\\v'-" DOUBLE_LINE_SEP "'"
2224 "\\h'|\\n[%1]u",
2225 column_divide_reg(start_col));
2226 if (left_adjust)
2227 prints(left_adjust);
2228 prints("'");
2229 printfs("\\D'l |\\n[%1]u",
2230 column_divide_reg(end_col));
2231 if (right_adjust)
2232 prints(right_adjust);
2233 prints(" 0'");
2234 printfs("\\v'" DOUBLE_LINE_SEP "'"
2235 "\\h'|\\n[%1]u",
2236 column_divide_reg(start_col));
2237 if (left_adjust)
2238 prints(left_adjust);
2239 prints("'");
2240 printfs("\\D'l |\\n[%1]u",
2241 column_divide_reg(end_col));
2242 if (right_adjust)
2243 prints(right_adjust);
2244 prints(" 0'");
2245 start_col = end_col;
2248 prints("\\s0\n"
2249 ".ls\n"
2250 ".vs\n");
2253 void table::compute_vrule_top_adjust(int start_row, int col, string &result)
2255 if (row_is_all_lines[start_row] && start_row < nrows - 1) {
2256 if (row_is_all_lines[start_row] == 2)
2257 result = "\\n[.v]u-" BODY_HEIGHT "-" BODY_DEPTH "+" DOUBLE_LINE_SEP;
2258 else
2259 result = "\\n[.v]u-" BODY_HEIGHT "-" BODY_DEPTH;
2260 start_row++;
2262 else {
2263 result = "";
2264 if (start_row == 0)
2265 return;
2266 for (stuff *p = stuff_list; p && p->row <= start_row; p = p->next)
2267 if (p->row == start_row
2268 && (p->is_single_line() || p->is_double_line()))
2269 return;
2271 int left = 0;
2272 if (col > 0) {
2273 table_entry *e = entry[start_row-1][col-1];
2274 if (e && e->start_row == e->end_row) {
2275 if (e->to_double_line_entry() != 0)
2276 left = 2;
2277 else if (e->to_single_line_entry() != 0)
2278 left = 1;
2281 int right = 0;
2282 if (col < ncolumns) {
2283 table_entry *e = entry[start_row-1][col];
2284 if (e && e->start_row == e->end_row) {
2285 if (e->to_double_line_entry() != 0)
2286 right = 2;
2287 else if (e->to_single_line_entry() != 0)
2288 right = 1;
2291 if (row_is_all_lines[start_row-1] == 0) {
2292 if (left > 0 || right > 0) {
2293 result += "-" BODY_DEPTH "-" BAR_HEIGHT;
2294 if ((left == 2 && right != 2) || (right == 2 && left != 2))
2295 result += "-" HALF_DOUBLE_LINE_SEP;
2296 else if (left == 2 && right == 2)
2297 result += "+" HALF_DOUBLE_LINE_SEP;
2300 else if (row_is_all_lines[start_row-1] == 2) {
2301 if ((left == 2 && right != 2) || (right == 2 && left != 2))
2302 result += "-" DOUBLE_LINE_SEP;
2303 else if (left == 1 || right == 1)
2304 result += "-" HALF_DOUBLE_LINE_SEP;
2308 void table::compute_vrule_bot_adjust(int end_row, int col, string &result)
2310 if (row_is_all_lines[end_row] && end_row > 0) {
2311 end_row--;
2312 result = "";
2314 else {
2315 for (stuff *p = stuff_list; p && p->row < end_row + 1; p = p->next)
2317 if (p && p->row == end_row + 1 && p->is_double_line()) {
2318 result = "-" DOUBLE_LINE_SEP;
2319 return;
2321 if ((p != 0 && p->row == end_row + 1)
2322 || end_row == nrows - 1) {
2323 result = "";
2324 return;
2326 if (row_is_all_lines[end_row+1] == 1)
2327 result = "\\n[.v]u-" BODY_HEIGHT "-" BODY_DEPTH;
2328 else if (row_is_all_lines[end_row+1] == 2)
2329 result = "\\n[.v]u-" BODY_HEIGHT "-" BODY_DEPTH "+" DOUBLE_LINE_SEP;
2330 else
2331 result = "";
2333 int left = 0;
2334 if (col > 0) {
2335 table_entry *e = entry[end_row+1][col-1];
2336 if (e && e->start_row == e->end_row) {
2337 if (e->to_double_line_entry() != 0)
2338 left = 2;
2339 else if (e->to_single_line_entry() != 0)
2340 left = 1;
2343 int right = 0;
2344 if (col < ncolumns) {
2345 table_entry *e = entry[end_row+1][col];
2346 if (e && e->start_row == e->end_row) {
2347 if (e->to_double_line_entry() != 0)
2348 right = 2;
2349 else if (e->to_single_line_entry() != 0)
2350 right = 1;
2353 if (row_is_all_lines[end_row+1] == 0) {
2354 if (left > 0 || right > 0) {
2355 result = "1v-" BODY_DEPTH "-" BAR_HEIGHT;
2356 if ((left == 2 && right != 2) || (right == 2 && left != 2))
2357 result += "+" HALF_DOUBLE_LINE_SEP;
2358 else if (left == 2 && right == 2)
2359 result += "-" HALF_DOUBLE_LINE_SEP;
2362 else if (row_is_all_lines[end_row+1] == 2) {
2363 if (left == 2 && right == 2)
2364 result += "-" DOUBLE_LINE_SEP;
2365 else if (left != 2 && right != 2 && (left == 1 || right == 1))
2366 result += "-" HALF_DOUBLE_LINE_SEP;
2370 void table::add_vertical_rule(int start_row, int end_row, int col, int is_double)
2372 vrule_list = new vertical_rule(start_row, end_row, col, is_double,
2373 vrule_list);
2374 compute_vrule_top_adjust(start_row, col, vrule_list->top_adjust);
2375 compute_vrule_bot_adjust(end_row, col, vrule_list->bot_adjust);
2378 void table::build_vrule_list()
2380 int col;
2381 if (flags & ALLBOX) {
2382 for (col = 1; col < ncolumns; col++) {
2383 int start_row = 0;
2384 for (;;) {
2385 while (start_row < nrows && vline_spanned(start_row, col))
2386 start_row++;
2387 if (start_row >= nrows)
2388 break;
2389 int end_row = start_row;
2390 while (end_row < nrows && !vline_spanned(end_row, col))
2391 end_row++;
2392 end_row--;
2393 add_vertical_rule(start_row, end_row, col, 0);
2394 start_row = end_row + 1;
2398 if (flags & (BOX|ALLBOX|DOUBLEBOX)) {
2399 add_vertical_rule(0, nrows - 1, 0, 0);
2400 add_vertical_rule(0, nrows - 1, ncolumns, 0);
2402 for (int end_row = 0; end_row < nrows; end_row++)
2403 for (col = 0; col < ncolumns+1; col++)
2404 if (vline[end_row][col] > 0
2405 && !vline_spanned(end_row, col)
2406 && (end_row == nrows - 1
2407 || vline[end_row+1][col] != vline[end_row][col]
2408 || vline_spanned(end_row+1, col))) {
2409 for (int start_row = end_row - 1;
2410 start_row >= 0
2411 && vline[start_row][col] == vline[end_row][col]
2412 && !vline_spanned(start_row, col);
2413 start_row--)
2415 start_row++;
2416 add_vertical_rule(start_row, end_row, col, vline[end_row][col] > 1);
2418 for (vertical_rule *p = vrule_list; p; p = p->next)
2419 if (p->is_double)
2420 for (int r = p->start_row; r <= p->end_row; r++) {
2421 if (p->col > 0 && entry[r][p->col-1] != 0
2422 && entry[r][p->col-1]->end_col == p->col-1) {
2423 int is_corner = r == p->start_row || r == p->end_row;
2424 entry[r][p->col-1]->note_double_vrule_on_right(is_corner);
2426 if (p->col < ncolumns && entry[r][p->col] != 0
2427 && entry[r][p->col]->start_col == p->col) {
2428 int is_corner = r == p->start_row || r == p->end_row;
2429 entry[r][p->col]->note_double_vrule_on_left(is_corner);
2434 void table::define_bottom_macro()
2436 prints(".eo\n"
2437 ".de T#\n"
2438 ".if !\\n[" SUPPRESS_BOTTOM_REG "] \\{"
2439 "." REPEATED_VPT_MACRO " 0\n"
2440 ".mk " SAVED_VERTICAL_POS_REG "\n");
2441 if (flags & (BOX|ALLBOX|DOUBLEBOX)) {
2442 prints(".if \\n[T.]&\\n[" NEED_BOTTOM_RULE_REG "] \\{");
2443 print_single_hline(0);
2444 prints(".\\}\n");
2446 prints(".ls 1\n");
2447 for (vertical_rule *p = vrule_list; p; p = p->next)
2448 p->contribute_to_bottom_macro(this);
2449 if (flags & DOUBLEBOX)
2450 prints(".if \\n[T.] \\{.vs " DOUBLE_LINE_SEP ">?\\n[.V]u\n"
2451 "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]"
2452 "\\D'l \\n[TW]u 0'\\s0\n"
2453 ".vs\n"
2454 ".\\}\n"
2455 ".if \\n[" LAST_PASSED_ROW_REG "]>=0 "
2456 ".nr " TOP_REG " \\n[#T]-" DOUBLE_LINE_SEP "\n"
2457 ".sp -1\n"
2458 "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]"
2459 "\\D'l 0 |\\n[" TOP_REG "]u-1v'\\s0\n"
2460 ".sp -1\n"
2461 "\\v'" BODY_DEPTH "'\\h'|\\n[TW]u'\\s[\\n[" LINESIZE_REG "]]"
2462 "\\D'l 0 |\\n[" TOP_REG "]u-1v'\\s0\n");
2463 prints(".ls\n");
2464 prints(".nr " LAST_PASSED_ROW_REG " \\n[" CURRENT_ROW_REG "]\n"
2465 ".sp |\\n[" SAVED_VERTICAL_POS_REG "]u\n"
2466 "." REPEATED_VPT_MACRO " 1\n"
2467 ".\\}\n"
2468 "..\n"
2469 ".ec\n");
2473 // is the vertical line before column c in row r horizontally spanned?
2475 int table::vline_spanned(int r, int c)
2477 assert(r >= 0 && r < nrows && c >= 0 && c < ncolumns + 1);
2478 return (c != 0 && c != ncolumns && entry[r][c] != 0
2479 && entry[r][c]->start_col != c
2480 // horizontally spanning lines don't count
2481 && entry[r][c]->to_double_line_entry() == 0
2482 && entry[r][c]->to_single_line_entry() == 0);
2485 int table::row_begins_section(int r)
2487 assert(r >= 0 && r < nrows);
2488 for (int i = 0; i < ncolumns; i++)
2489 if (entry[r][i] && entry[r][i]->start_row != r)
2490 return 0;
2491 return 1;
2494 int table::row_ends_section(int r)
2496 assert(r >= 0 && r < nrows);
2497 for (int i = 0; i < ncolumns; i++)
2498 if (entry[r][i] && entry[r][i]->end_row != r)
2499 return 0;
2500 return 1;
2503 void table::do_row(int r)
2505 if (row_begins_section(r))
2506 prints("." KEEP_MACRO_NAME "\n");
2507 int had_line = 0;
2508 for (stuff *p = stuff_list; p && p->row < r; p = p->next)
2510 for (stuff *p1 = p; p1 && p1->row == r; p1 = p1->next)
2511 if (!p1->printed && (p1->is_single_line() || p1->is_double_line())) {
2512 had_line = 1;
2513 break;
2515 if (!had_line && !row_is_all_lines[r])
2516 printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r));
2517 had_line = 0;
2518 printfs("\\*[" TRANSPARENT_STRING_NAME "].nr " CURRENT_ROW_REG " %1\n",
2519 as_string(r));
2520 for (; p && p->row == r; p = p->next)
2521 if (!p->printed) {
2522 p->print(this);
2523 if (!had_line && (p->is_single_line() || p->is_double_line())) {
2524 printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r));
2525 had_line = 1;
2528 if (!had_line && row_is_all_lines[r])
2529 printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r));
2530 // we might have had a .TH, for example, since we last tried
2531 if (row_begins_section(r))
2532 prints("." KEEP_MACRO_NAME "\n");
2533 printfs(".mk %1\n", row_start_reg(r));
2534 prints(".mk " BOTTOM_REG "\n"
2535 "." REPEATED_VPT_MACRO " 0\n");
2536 int c;
2537 int row_is_blank = 1;
2538 int first_start_row = r;
2539 for (c = 0; c < ncolumns; c++) {
2540 table_entry *e = entry[r][c];
2541 if (e) {
2542 if (e->end_row == r) {
2543 e->do_depth();
2544 if (e->start_row < first_start_row)
2545 first_start_row = e->start_row;
2546 row_is_blank = 0;
2548 c = e->end_col;
2551 if (row_is_blank)
2552 prints(".nr " BOTTOM_REG " +1v\n");
2553 if (row_is_all_lines[r]) {
2554 prints(".vs \\n[.v]u-" BODY_HEIGHT "-" BODY_DEPTH);
2555 if (row_is_all_lines[r] == 2)
2556 prints("+" DOUBLE_LINE_SEP);
2557 prints(">?\\n[.V]u\n.ls 1\n");
2558 prints("\\&");
2559 prints("\\v'" BODY_DEPTH);
2560 if (row_is_all_lines[r] == 2)
2561 prints("-" HALF_DOUBLE_LINE_SEP);
2562 prints("'");
2563 for (c = 0; c < ncolumns; c++) {
2564 table_entry *e = entry[r][c];
2565 if (e) {
2566 if (e->end_row == e->start_row)
2567 e->to_simple_entry()->simple_print(1);
2568 c = e->end_col;
2571 prints("\n");
2572 prints(".ls\n"
2573 ".vs\n");
2574 prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n");
2575 printfs(".sp |\\n[%1]u\n", row_start_reg(r));
2577 for (int i = row_is_all_lines[r] ? r - 1 : r;
2578 i >= first_start_row;
2579 i--) {
2580 simple_entry *first = 0;
2581 for (c = 0; c < ncolumns; c++) {
2582 table_entry *e = entry[r][c];
2583 if (e) {
2584 if (e->end_row == r && e->start_row == i) {
2585 simple_entry *simple = e->to_simple_entry();
2586 if (simple) {
2587 if (!first) {
2588 prints(".ta");
2589 first = simple;
2591 simple->add_tab();
2594 c = e->end_col;
2597 if (first) {
2598 prints('\n');
2599 first->position_vertically();
2600 first->set_location();
2601 prints("\\&");
2602 first->simple_print(0);
2603 for (c = first->end_col + 1; c < ncolumns; c++) {
2604 table_entry *e = entry[r][c];
2605 if (e) {
2606 if (e->end_row == r && e->start_row == i) {
2607 simple_entry *simple = e->to_simple_entry();
2608 if (simple)
2609 simple->simple_print(0);
2611 c = e->end_col;
2614 prints('\n');
2615 prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n");
2616 printfs(".sp |\\n[%1]u\n", row_start_reg(r));
2619 for (c = 0; c < ncolumns; c++) {
2620 table_entry *e = entry[r][c];
2621 if (e) {
2622 if (e->end_row == r && e->to_simple_entry() == 0) {
2623 e->position_vertically();
2624 e->print();
2625 prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n");
2626 printfs(".sp |\\n[%1]u\n", row_start_reg(r));
2628 c = e->end_col;
2631 prints("." REPEATED_VPT_MACRO " 1\n"
2632 ".sp |\\n[" BOTTOM_REG "]u\n"
2633 "\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG " 1\n");
2634 if (r != nrows - 1 && (flags & ALLBOX)) {
2635 print_single_hline(r + 1);
2636 prints("\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG " 0\n");
2638 if (r != nrows - 1) {
2639 if (p && p->row == r + 1
2640 && (p->is_single_line() || p->is_double_line())) {
2641 p->print(this);
2642 prints("\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG
2643 " 0\n");
2645 int printed_one = 0;
2646 for (vertical_rule *p = vrule_list; p; p = p->next)
2647 if (p->end_row == r) {
2648 if (!printed_one) {
2649 prints("." REPEATED_VPT_MACRO " 0\n");
2650 printed_one = 1;
2652 p->print();
2654 if (printed_one)
2655 prints("." REPEATED_VPT_MACRO " 1\n");
2656 if (row_ends_section(r))
2657 prints("." RELEASE_MACRO_NAME "\n");
2661 void table::do_top()
2663 prints(".fc \002\003\n");
2664 if (flags & (BOX|DOUBLEBOX|ALLBOX))
2665 prints("." TABLE_KEEP_MACRO_NAME "\n");
2666 if (flags & DOUBLEBOX) {
2667 prints(".ls 1\n"
2668 ".vs \\n[.v]u-" BODY_HEIGHT "-" BODY_DEPTH ">?\\n[.V]u\n"
2669 "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]\\D'l \\n[TW]u 0'\n"
2670 ".vs\n"
2671 "." REPEATED_MARK_MACRO " " TOP_REG "\n"
2672 ".vs " DOUBLE_LINE_SEP ">?\\n[.V]u\n");
2673 printfs("\\v'" BODY_DEPTH "'"
2674 "\\s[\\n[" LINESIZE_REG "]]"
2675 "\\h'\\n[%1]u'"
2676 "\\D'l |\\n[%2]u 0'"
2677 "\\s0"
2678 "\n",
2679 column_divide_reg(0),
2680 column_divide_reg(ncolumns));
2681 prints(".ls\n"
2682 ".vs\n");
2684 else if (flags & (ALLBOX|BOX)) {
2685 print_single_hline(0);
2687 //printfs(".mk %1\n", row_top_reg(0));
2690 void table::do_bottom()
2692 // print stuff after last row
2693 for (stuff *p = stuff_list; p; p = p->next)
2694 if (p->row > nrows - 1)
2695 p->print(this);
2696 prints("." RELEASE_MACRO_NAME "\n");
2697 printfs(".mk %1\n", row_top_reg(nrows));
2698 prints(".nr " NEED_BOTTOM_RULE_REG " 1\n"
2699 ".nr T. 1\n"
2700 ".T#\n");
2701 if (flags & (BOX|DOUBLEBOX|ALLBOX))
2702 prints("." TABLE_RELEASE_MACRO_NAME "\n");
2703 if (flags & DOUBLEBOX)
2704 prints(".sp " DOUBLE_LINE_SEP "\n");
2705 prints("." RESET_MACRO_NAME "\n"
2706 ".fc\n"
2707 ".cp \\n(" COMPATIBLE_REG "\n");
2710 int table::get_nrows()
2712 return nrows;
2715 const char *last_filename = 0;
2717 void set_troff_location(const char *fn, int ln)
2719 if (!location_force_filename && last_filename != 0
2720 && strcmp(fn, last_filename) == 0)
2721 printfs(".lf %1\n", as_string(ln));
2722 else {
2723 printfs(".lf %1 %2\n", as_string(ln), fn);
2724 last_filename = fn;
2725 location_force_filename = 0;
2729 void printfs(const char *s, const string &arg1, const string &arg2,
2730 const string &arg3, const string &arg4, const string &arg5)
2732 if (s) {
2733 char c;
2734 while ((c = *s++) != '\0') {
2735 if (c == '%') {
2736 switch (*s++) {
2737 case '1':
2738 prints(arg1);
2739 break;
2740 case '2':
2741 prints(arg2);
2742 break;
2743 case '3':
2744 prints(arg3);
2745 break;
2746 case '4':
2747 prints(arg4);
2748 break;
2749 case '5':
2750 prints(arg5);
2751 break;
2752 case '6':
2753 case '7':
2754 case '8':
2755 case '9':
2756 break;
2757 case '%':
2758 prints('%');
2759 break;
2760 default:
2761 assert(0);
2764 else
2765 prints(c);