Merge branch 'topic/sync-to-go-2'
[s-roff.git] / src / pre-tbl / table.cpp
blob7abe4fabea41072bf366cd83935f46e6e4aead44
1 /*
2 * Copyright (c) 2014 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
4 * Copyright (C) 1989 - 1992, 2000, 2003, 2004, 2007 - 2009
5 * Free Software Foundation, Inc.
6 * Written by James Clark (jjc@jclark.com)
8 * This is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License as published by the Free
10 * Software Foundation; either version 2, or (at your option) any later
11 * version.
13 * This is distributed in the hope that it will be useful, but WITHOUT ANY
14 * WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 * for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with groff; see the file COPYING. If not, write to the Free Software
20 * Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA.
23 #include "config.h"
24 #include "tbl-config.h"
26 #include "table.h"
28 #define BAR_HEIGHT ".25m"
29 #define DOUBLE_LINE_SEP "2p"
30 #define HALF_DOUBLE_LINE_SEP "1p"
31 #define LINE_SEP "2p"
32 #define BODY_DEPTH ".25m"
34 const int DEFAULT_COLUMN_SEPARATION = 3;
36 #define DELIMITER_CHAR "\\[tbl]"
37 #define SEPARATION_FACTOR_REG PREFIX "sep"
38 #define BOTTOM_REG PREFIX "bot"
39 #define RESET_MACRO_NAME PREFIX "init"
40 #define LINESIZE_REG PREFIX "lps"
41 #define TOP_REG PREFIX "top"
42 #define CURRENT_ROW_REG PREFIX "crow"
43 #define LAST_PASSED_ROW_REG PREFIX "passed"
44 #define TRANSPARENT_STRING_NAME PREFIX "trans"
45 #define QUOTE_STRING_NAME PREFIX "quote"
46 #define SECTION_DIVERSION_NAME PREFIX "section"
47 #define SECTION_DIVERSION_FLAG_REG PREFIX "sflag"
48 #define SAVED_VERTICAL_POS_REG PREFIX "vert"
49 #define NEED_BOTTOM_RULE_REG PREFIX "brule"
50 #define KEEP_MACRO_NAME PREFIX "keep"
51 #define RELEASE_MACRO_NAME PREFIX "release"
52 #define SAVED_FONT_REG PREFIX "fnt"
53 #define SAVED_SIZE_REG PREFIX "sz"
54 #define SAVED_FILL_REG PREFIX "fll"
55 #define SAVED_INDENT_REG PREFIX "ind"
56 #define SAVED_CENTER_REG PREFIX "cent"
57 #define TABLE_DIVERSION_NAME PREFIX "table"
58 #define TABLE_DIVERSION_FLAG_REG PREFIX "tflag"
59 #define TABLE_KEEP_MACRO_NAME PREFIX "tkeep"
60 #define TABLE_RELEASE_MACRO_NAME PREFIX "trelease"
61 #define NEEDED_REG PREFIX "needed"
62 #define REPEATED_MARK_MACRO PREFIX "rmk"
63 #define REPEATED_VPT_MACRO PREFIX "rvpt"
64 #define SUPPRESS_BOTTOM_REG PREFIX "supbot"
65 #define SAVED_DN_REG PREFIX "dn"
67 // this must be one character
68 #define COMPATIBLE_REG PREFIX "c"
70 #define EXPAND_REG PREFIX "expand"
72 #define LEADER_REG PREFIX LEADER
74 #define BLOCK_WIDTH_PREFIX PREFIX "tbw"
75 #define BLOCK_DIVERSION_PREFIX PREFIX "tbd"
76 #define BLOCK_HEIGHT_PREFIX PREFIX "tbh"
77 #define SPAN_WIDTH_PREFIX PREFIX "w"
78 #define SPAN_LEFT_NUMERIC_WIDTH_PREFIX PREFIX "lnw"
79 #define SPAN_RIGHT_NUMERIC_WIDTH_PREFIX PREFIX "rnw"
80 #define SPAN_ALPHABETIC_WIDTH_PREFIX PREFIX "aw"
81 #define COLUMN_SEPARATION_PREFIX PREFIX "cs"
82 #define ROW_START_PREFIX PREFIX "rs"
83 #define COLUMN_START_PREFIX PREFIX "cl"
84 #define COLUMN_END_PREFIX PREFIX "ce"
85 #define COLUMN_DIVIDE_PREFIX PREFIX "cd"
86 #define ROW_TOP_PREFIX PREFIX "rt"
88 string block_width_reg(int, int);
89 string block_diversion_name(int, int);
90 string block_height_reg(int, int);
91 string span_width_reg(int, int);
92 string span_left_numeric_width_reg(int, int);
93 string span_right_numeric_width_reg(int, int);
94 string span_alphabetic_width_reg(int, int);
95 string column_separation_reg(int);
96 string row_start_reg(int);
97 string column_start_reg(int);
98 string column_end_reg(int);
99 string column_divide_reg(int);
100 string row_top_reg(int);
102 void set_inline_modifier(const entry_modifier *);
103 void restore_inline_modifier(const entry_modifier *);
104 void set_modifier(const entry_modifier *);
105 int find_decimal_point(const char *, char, const char *);
107 string an_empty_string;
108 int location_force_filename = 0;
110 void printfs(const char *,
111 const string &arg1 = an_empty_string,
112 const string &arg2 = an_empty_string,
113 const string &arg3 = an_empty_string,
114 const string &arg4 = an_empty_string,
115 const string &arg5 = an_empty_string);
117 void prints(const string &);
119 inline void prints(char c)
121 putchar(c);
124 inline void prints(const char *s)
126 fputs(s, stdout);
129 void prints(const string &s)
131 if (!s.empty())
132 fwrite(s.contents(), 1, s.length(), stdout);
135 struct horizontal_span {
136 horizontal_span *next;
137 int start_col;
138 int end_col;
139 horizontal_span(int, int, horizontal_span *);
142 class single_line_entry;
143 class double_line_entry;
144 class simple_entry;
146 class table_entry
148 friend class table;
150 table_entry *next;
151 int input_lineno;
152 const char *input_filename;
154 protected:
155 int start_row;
156 int end_row;
157 int start_col;
158 int end_col;
159 const table *parent;
160 const entry_modifier *mod;
162 public:
163 void set_location();
164 table_entry(const table *, const entry_modifier *);
165 virtual ~table_entry();
166 virtual int divert(int, const string *, int *, int);
167 virtual void do_width();
168 virtual void do_depth();
169 virtual void print() = 0;
170 virtual void position_vertically() = 0;
171 virtual single_line_entry *to_single_line_entry();
172 virtual double_line_entry *to_double_line_entry();
173 virtual simple_entry *to_simple_entry();
174 virtual int line_type();
175 virtual void note_double_vrule_on_right(int);
176 virtual void note_double_vrule_on_left(int);
179 class simple_entry
180 : public table_entry
182 public:
183 simple_entry(const table *, const entry_modifier *);
184 void print();
185 void position_vertically();
186 simple_entry *to_simple_entry();
187 virtual void add_tab();
188 virtual void simple_print(int);
191 class empty_entry
192 : public simple_entry
194 public:
195 empty_entry(const table *, const entry_modifier *);
196 int line_type();
199 class text_entry
200 : public simple_entry
202 protected:
203 char *contents;
205 void print_contents();
207 public:
208 text_entry(const table *, const entry_modifier *, char *);
209 ~text_entry();
212 void text_entry::print_contents()
214 set_inline_modifier(mod);
215 prints(contents);
216 restore_inline_modifier(mod);
219 class repeated_char_entry
220 : public text_entry
222 public:
223 repeated_char_entry(const table *, const entry_modifier *, char *);
224 void simple_print(int);
227 class simple_text_entry
228 : public text_entry
230 public:
231 simple_text_entry(const table *, const entry_modifier *, char *);
232 void do_width();
235 class left_text_entry
236 : public simple_text_entry
238 public:
239 left_text_entry(const table *, const entry_modifier *, char *);
240 void simple_print(int);
241 void add_tab();
244 class right_text_entry
245 : public simple_text_entry
247 public:
248 right_text_entry(const table *, const entry_modifier *, char *);
249 void simple_print(int);
250 void add_tab();
253 class center_text_entry
254 : public simple_text_entry
256 public:
257 center_text_entry(const table *, const entry_modifier *, char *);
258 void simple_print(int);
259 void add_tab();
262 class numeric_text_entry
263 : public text_entry
265 int dot_pos;
267 public:
268 numeric_text_entry(const table *, const entry_modifier *, char *, int);
269 void do_width();
270 void simple_print(int);
273 class alphabetic_text_entry
274 : public text_entry
276 public:
277 alphabetic_text_entry(const table *, const entry_modifier *, char *);
278 void do_width();
279 void simple_print(int);
280 void add_tab();
283 class line_entry
284 : public simple_entry
286 protected:
287 char double_vrule_on_right;
288 char double_vrule_on_left;
290 public:
291 line_entry(const table *, const entry_modifier *);
292 void note_double_vrule_on_right(int);
293 void note_double_vrule_on_left(int);
294 void simple_print(int) = 0;
297 class single_line_entry
298 : public line_entry
300 public:
301 single_line_entry(const table *, const entry_modifier *);
302 void simple_print(int);
303 single_line_entry *to_single_line_entry();
304 int line_type();
307 class double_line_entry
308 : public line_entry
310 public:
311 double_line_entry(const table *, const entry_modifier *);
312 void simple_print(int);
313 double_line_entry *to_double_line_entry();
314 int line_type();
317 class short_line_entry
318 : public simple_entry
320 public:
321 short_line_entry(const table *, const entry_modifier *);
322 void simple_print(int);
323 int line_type();
326 class short_double_line_entry
327 : public simple_entry
329 public:
330 short_double_line_entry(const table *, const entry_modifier *);
331 void simple_print(int);
332 int line_type();
335 class block_entry
336 : public table_entry
338 char *contents;
340 protected:
341 void do_divert(int, int, const string *, int *, int);
343 public:
344 block_entry(const table *, const entry_modifier *, char *);
345 ~block_entry();
346 int divert(int, const string *, int *, int);
347 void do_depth();
348 void position_vertically();
349 void print() = 0;
352 class left_block_entry
353 : public block_entry
355 public:
356 left_block_entry(const table *, const entry_modifier *, char *);
357 void print();
360 class right_block_entry
361 : public block_entry
363 public:
364 right_block_entry(const table *, const entry_modifier *, char *);
365 void print();
368 class center_block_entry
369 : public block_entry
371 public:
372 center_block_entry(const table *, const entry_modifier *, char *);
373 void print();
376 class alphabetic_block_entry
377 : public block_entry
379 public:
380 alphabetic_block_entry(const table *, const entry_modifier *, char *);
381 void print();
382 int divert(int, const string *, int *, int);
385 table_entry::table_entry(const table *p, const entry_modifier *m)
386 : next(0), input_lineno(-1), input_filename(0),
387 start_row(-1), end_row(-1), start_col(-1), end_col(-1), parent(p), mod(m)
391 table_entry::~table_entry()
395 int table_entry::divert(int, const string *, int *, int)
397 return 0;
400 void table_entry::do_width()
404 single_line_entry *table_entry::to_single_line_entry()
406 return 0;
409 double_line_entry *table_entry::to_double_line_entry()
411 return 0;
414 simple_entry *table_entry::to_simple_entry()
416 return 0;
419 void table_entry::do_depth()
423 void table_entry::set_location()
425 set_troff_location(input_filename, input_lineno);
428 int table_entry::line_type()
430 return -1;
433 void table_entry::note_double_vrule_on_right(int)
437 void table_entry::note_double_vrule_on_left(int)
441 simple_entry::simple_entry(const table *p, const entry_modifier *m)
442 : table_entry(p, m)
446 void simple_entry::add_tab()
448 // do nothing
451 void simple_entry::simple_print(int)
453 // do nothing
456 void simple_entry::position_vertically()
458 if (start_row != end_row)
459 switch (mod->vertical_alignment) {
460 case entry_modifier::TOP:
461 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
462 break;
463 case entry_modifier::CENTER:
464 // Peform the motion in two stages so that the center is rounded
465 // vertically upwards even if net vertical motion is upwards.
466 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
467 printfs(".sp \\n[" BOTTOM_REG "]u-\\n[%1]u-1v/2u\n",
468 row_start_reg(start_row));
469 break;
470 case entry_modifier::BOTTOM:
471 printfs(".sp |\\n[%1]u+\\n[" BOTTOM_REG "]u-\\n[%1]u-1v\n",
472 row_start_reg(start_row));
473 break;
474 default:
475 assert(0);
479 void simple_entry::print()
481 prints(".ta");
482 add_tab();
483 prints('\n');
484 set_location();
485 prints("\\&");
486 simple_print(0);
487 prints('\n');
490 simple_entry *simple_entry::to_simple_entry()
492 return this;
495 empty_entry::empty_entry(const table *p, const entry_modifier *m)
496 : simple_entry(p, m)
500 int empty_entry::line_type()
502 return 0;
505 text_entry::text_entry(const table *p, const entry_modifier *m, char *s)
506 : simple_entry(p, m), contents(s)
510 text_entry::~text_entry()
512 a_delete contents;
515 repeated_char_entry::repeated_char_entry(const table *p,
516 const entry_modifier *m, char *s)
517 : text_entry(p, m, s)
521 void repeated_char_entry::simple_print(int)
523 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
524 set_inline_modifier(mod);
525 printfs("\\l" DELIMITER_CHAR "\\n[%1]u\\&",
526 span_width_reg(start_col, end_col));
527 prints(contents);
528 prints(DELIMITER_CHAR);
529 restore_inline_modifier(mod);
532 simple_text_entry::simple_text_entry(const table *p,
533 const entry_modifier *m, char *s)
534 : text_entry(p, m, s)
538 void simple_text_entry::do_width()
540 set_location();
541 printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR,
542 span_width_reg(start_col, end_col));
543 print_contents();
544 prints(DELIMITER_CHAR "\n");
547 left_text_entry::left_text_entry(const table *p,
548 const entry_modifier *m, char *s)
549 : simple_text_entry(p, m, s)
553 void left_text_entry::simple_print(int)
555 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
556 print_contents();
559 // The only point of this is to make `\a' ``work'' as in Unix tbl. Grrr.
561 void left_text_entry::add_tab()
563 printfs(" \\n[%1]u", column_end_reg(end_col));
566 right_text_entry::right_text_entry(const table *p,
567 const entry_modifier *m, char *s)
568 : simple_text_entry(p, m, s)
572 void right_text_entry::simple_print(int)
574 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
575 prints("\002\003");
576 print_contents();
577 prints("\002");
580 void right_text_entry::add_tab()
582 printfs(" \\n[%1]u", column_end_reg(end_col));
585 center_text_entry::center_text_entry(const table *p,
586 const entry_modifier *m, char *s)
587 : simple_text_entry(p, m, s)
591 void center_text_entry::simple_print(int)
593 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
594 prints("\002\003");
595 print_contents();
596 prints("\003\002");
599 void center_text_entry::add_tab()
601 printfs(" \\n[%1]u", column_end_reg(end_col));
604 numeric_text_entry::numeric_text_entry(const table *p,
605 const entry_modifier *m,
606 char *s, int pos)
607 : text_entry(p, m, s), dot_pos(pos)
611 void numeric_text_entry::do_width()
613 if (dot_pos != 0) {
614 set_location();
615 printfs(".nr %1 0\\w" DELIMITER_CHAR,
616 block_width_reg(start_row, start_col));
617 set_inline_modifier(mod);
618 for (int i = 0; i < dot_pos; i++)
619 prints(contents[i]);
620 restore_inline_modifier(mod);
621 prints(DELIMITER_CHAR "\n");
622 printfs(".nr %1 \\n[%1]>?\\n[%2]\n",
623 span_left_numeric_width_reg(start_col, end_col),
624 block_width_reg(start_row, start_col));
626 else
627 printfs(".nr %1 0\n", block_width_reg(start_row, start_col));
628 if (contents[dot_pos] != '\0') {
629 set_location();
630 printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR,
631 span_right_numeric_width_reg(start_col, end_col));
632 set_inline_modifier(mod);
633 prints(contents + dot_pos);
634 restore_inline_modifier(mod);
635 prints(DELIMITER_CHAR "\n");
639 void numeric_text_entry::simple_print(int)
641 printfs("\\h'|(\\n[%1]u-\\n[%2]u-\\n[%3]u/2u+\\n[%2]u+\\n[%4]u-\\n[%5]u)'",
642 span_width_reg(start_col, end_col),
643 span_left_numeric_width_reg(start_col, end_col),
644 span_right_numeric_width_reg(start_col, end_col),
645 column_start_reg(start_col),
646 block_width_reg(start_row, start_col));
647 print_contents();
650 alphabetic_text_entry::alphabetic_text_entry(const table *p,
651 const entry_modifier *m,
652 char *s)
653 : text_entry(p, m, s)
657 void alphabetic_text_entry::do_width()
659 set_location();
660 printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR,
661 span_alphabetic_width_reg(start_col, end_col));
662 print_contents();
663 prints(DELIMITER_CHAR "\n");
666 void alphabetic_text_entry::simple_print(int)
668 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
669 printfs("\\h'\\n[%1]u-\\n[%2]u/2u'",
670 span_width_reg(start_col, end_col),
671 span_alphabetic_width_reg(start_col, end_col));
672 print_contents();
675 // The only point of this is to make `\a' ``work'' as in Unix tbl. Grrr.
677 void alphabetic_text_entry::add_tab()
679 printfs(" \\n[%1]u", column_end_reg(end_col));
682 block_entry::block_entry(const table *p, const entry_modifier *m, char *s)
683 : table_entry(p, m), contents(s)
687 block_entry::~block_entry()
689 a_delete contents;
692 void block_entry::position_vertically()
694 if (start_row != end_row)
695 switch(mod->vertical_alignment) {
696 case entry_modifier::TOP:
697 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
698 break;
699 case entry_modifier::CENTER:
700 // Peform the motion in two stages so that the center is rounded
701 // vertically upwards even if net vertical motion is upwards.
702 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
703 printfs(".sp \\n[" BOTTOM_REG "]u-\\n[%1]u-\\n[%2]u/2u\n",
704 row_start_reg(start_row),
705 block_height_reg(start_row, start_col));
706 break;
707 case entry_modifier::BOTTOM:
708 printfs(".sp |\\n[%1]u+\\n[" BOTTOM_REG "]u-\\n[%1]u-\\n[%2]u\n",
709 row_start_reg(start_row),
710 block_height_reg(start_row, start_col));
711 break;
712 default:
713 assert(0);
715 if (mod->stagger)
716 prints(".sp -.5v\n");
719 int block_entry::divert(int ncols, const string *mw, int *sep, int do_expand)
721 do_divert(0, ncols, mw, sep, do_expand);
722 return 1;
725 void block_entry::do_divert(int alphabetic, int ncols, const string *mw,
726 int *sep, int do_expand)
728 int i;
729 for (i = start_col; i <= end_col; i++)
730 if (parent->expand[i])
731 break;
732 if (i > end_col) {
733 if (do_expand)
734 return;
736 else {
737 if (!do_expand)
738 return;
740 printfs(".di %1\n", block_diversion_name(start_row, start_col));
741 prints(".if \\n[" SAVED_FILL_REG "] .fi\n"
742 ".in 0\n");
743 prints(".ll ");
744 for (i = start_col; i <= end_col; i++)
745 if (mw[i].empty() && !parent->expand[i])
746 break;
747 if (i > end_col) {
748 // Every column spanned by this entry has a minimum width.
749 for (int j = start_col; j <= end_col; j++) {
750 if (j > start_col) {
751 if (sep)
752 printfs("+%1n", as_string(sep[j - 1]));
753 prints('+');
755 if (parent->expand[j])
756 prints("\\n[" EXPAND_REG "]u");
757 else
758 printfs("(n;%1)", mw[j]);
760 printfs(">?\\n[%1]u", span_width_reg(start_col, end_col));
762 else
763 // Assign each column with a block entry 1/(n+1) of the line
764 // width, where n is the column count.
765 printfs("(u;\\n[%1]>?(\\n[.l]*%2/%3))",
766 span_width_reg(start_col, end_col),
767 as_string(end_col - start_col + 1),
768 as_string(ncols + 1));
769 if (alphabetic)
770 prints("-2n");
771 prints("\n");
772 prints(".cp \\n(" COMPATIBLE_REG "\n");
773 set_modifier(mod);
774 set_location();
775 prints(contents);
776 prints(".br\n.di\n.cp 0\n");
777 if (!mod->zero_width) {
778 if (alphabetic) {
779 printfs(".nr %1 \\n[%1]>?(\\n[dl]+2n)\n",
780 span_width_reg(start_col, end_col));
781 printfs(".nr %1 \\n[%1]>?\\n[dl]\n",
782 span_alphabetic_width_reg(start_col, end_col));
784 else
785 printfs(".nr %1 \\n[%1]>?\\n[dl]\n",
786 span_width_reg(start_col, end_col));
788 printfs(".nr %1 \\n[dn]\n", block_height_reg(start_row, start_col));
789 printfs(".nr %1 \\n[dl]\n", block_width_reg(start_row, start_col));
790 prints("." RESET_MACRO_NAME "\n"
791 ".in \\n[" SAVED_INDENT_REG "]u\n"
792 ".nf\n");
793 // the block might have contained .lf commands
794 location_force_filename = 1;
797 void block_entry::do_depth()
799 printfs(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?(\\n[%1]+\\n[%2])\n",
800 row_start_reg(start_row),
801 block_height_reg(start_row, start_col));
804 left_block_entry::left_block_entry(const table *p,
805 const entry_modifier *m, char *s)
806 : block_entry(p, m, s)
810 void left_block_entry::print()
812 printfs(".in +\\n[%1]u\n", column_start_reg(start_col));
813 printfs(".%1\n", block_diversion_name(start_row, start_col));
814 prints(".in\n");
817 right_block_entry::right_block_entry(const table *p,
818 const entry_modifier *m, char *s)
819 : block_entry(p, m, s)
823 void right_block_entry::print()
825 printfs(".in +\\n[%1]u+\\n[%2]u-\\n[%3]u\n",
826 column_start_reg(start_col),
827 span_width_reg(start_col, end_col),
828 block_width_reg(start_row, start_col));
829 printfs(".%1\n", block_diversion_name(start_row, start_col));
830 prints(".in\n");
833 center_block_entry::center_block_entry(const table *p,
834 const entry_modifier *m, char *s)
835 : block_entry(p, m, s)
839 void center_block_entry::print()
841 printfs(".in +\\n[%1]u+(\\n[%2]u-\\n[%3]u/2u)\n",
842 column_start_reg(start_col),
843 span_width_reg(start_col, end_col),
844 block_width_reg(start_row, start_col));
845 printfs(".%1\n", block_diversion_name(start_row, start_col));
846 prints(".in\n");
849 alphabetic_block_entry::alphabetic_block_entry(const table *p,
850 const entry_modifier *m,
851 char *s)
852 : block_entry(p, m, s)
856 int alphabetic_block_entry::divert(int ncols, const string *mw, int *sep,
857 int do_expand)
859 do_divert(1, ncols, mw, sep, do_expand);
860 return 1;
863 void alphabetic_block_entry::print()
865 printfs(".in +\\n[%1]u+(\\n[%2]u-\\n[%3]u/2u)\n",
866 column_start_reg(start_col),
867 span_width_reg(start_col, end_col),
868 span_alphabetic_width_reg(start_col, end_col));
869 printfs(".%1\n", block_diversion_name(start_row, start_col));
870 prints(".in\n");
873 line_entry::line_entry(const table *p, const entry_modifier *m)
874 : simple_entry(p, m), double_vrule_on_right(0), double_vrule_on_left(0)
878 void line_entry::note_double_vrule_on_right(int is_corner)
880 double_vrule_on_right = is_corner ? 1 : 2;
883 void line_entry::note_double_vrule_on_left(int is_corner)
885 double_vrule_on_left = is_corner ? 1 : 2;
888 single_line_entry::single_line_entry(const table *p, const entry_modifier *m)
889 : line_entry(p, m)
893 int single_line_entry::line_type()
895 return 1;
898 void single_line_entry::simple_print(int dont_move)
900 printfs("\\h'|\\n[%1]u",
901 column_divide_reg(start_col));
902 if (double_vrule_on_left) {
903 prints(double_vrule_on_left == 1 ? "-" : "+");
904 prints(HALF_DOUBLE_LINE_SEP);
906 prints("'");
907 if (!dont_move)
908 prints("\\v'-" BAR_HEIGHT "'");
909 printfs("\\s[\\n[" LINESIZE_REG "]]" "\\D'l |\\n[%1]u",
910 column_divide_reg(end_col+1));
911 if (double_vrule_on_right) {
912 prints(double_vrule_on_left == 1 ? "+" : "-");
913 prints(HALF_DOUBLE_LINE_SEP);
915 prints("0'\\s0");
916 if (!dont_move)
917 prints("\\v'" BAR_HEIGHT "'");
920 single_line_entry *single_line_entry::to_single_line_entry()
922 return this;
925 double_line_entry::double_line_entry(const table *p, const entry_modifier *m)
926 : line_entry(p, m)
930 int double_line_entry::line_type()
932 return 2;
935 void double_line_entry::simple_print(int dont_move)
937 if (!dont_move)
938 prints("\\v'-" BAR_HEIGHT "'");
939 printfs("\\h'|\\n[%1]u",
940 column_divide_reg(start_col));
941 if (double_vrule_on_left) {
942 prints(double_vrule_on_left == 1 ? "-" : "+");
943 prints(HALF_DOUBLE_LINE_SEP);
945 prints("'");
946 printfs("\\v'-" HALF_DOUBLE_LINE_SEP "'"
947 "\\s[\\n[" LINESIZE_REG "]]"
948 "\\D'l |\\n[%1]u",
949 column_divide_reg(end_col+1));
950 if (double_vrule_on_right)
951 prints("-" HALF_DOUBLE_LINE_SEP);
952 prints(" 0'");
953 printfs("\\v'" DOUBLE_LINE_SEP "'"
954 "\\D'l |\\n[%1]u",
955 column_divide_reg(start_col));
956 if (double_vrule_on_right) {
957 prints(double_vrule_on_left == 1 ? "+" : "-");
958 prints(HALF_DOUBLE_LINE_SEP);
960 prints(" 0'");
961 prints("\\s0"
962 "\\v'-" HALF_DOUBLE_LINE_SEP "'");
963 if (!dont_move)
964 prints("\\v'" BAR_HEIGHT "'");
967 double_line_entry *double_line_entry::to_double_line_entry()
969 return this;
972 short_line_entry::short_line_entry(const table *p, const entry_modifier *m)
973 : simple_entry(p, m)
977 int short_line_entry::line_type()
979 return 1;
982 void short_line_entry::simple_print(int dont_move)
984 if (mod->stagger)
985 prints("\\v'-.5v'");
986 if (!dont_move)
987 prints("\\v'-" BAR_HEIGHT "'");
988 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
989 printfs("\\s[\\n[" LINESIZE_REG "]]"
990 "\\D'l \\n[%1]u 0'"
991 "\\s0",
992 span_width_reg(start_col, end_col));
993 if (!dont_move)
994 prints("\\v'" BAR_HEIGHT "'");
995 if (mod->stagger)
996 prints("\\v'.5v'");
999 short_double_line_entry::short_double_line_entry(const table *p,
1000 const entry_modifier *m)
1001 : simple_entry(p, m)
1005 int short_double_line_entry::line_type()
1007 return 2;
1010 void short_double_line_entry::simple_print(int dont_move)
1012 if (mod->stagger)
1013 prints("\\v'-.5v'");
1014 if (!dont_move)
1015 prints("\\v'-" BAR_HEIGHT "'");
1016 printfs("\\h'|\\n[%2]u'"
1017 "\\v'-" HALF_DOUBLE_LINE_SEP "'"
1018 "\\s[\\n[" LINESIZE_REG "]]"
1019 "\\D'l \\n[%1]u 0'"
1020 "\\v'" DOUBLE_LINE_SEP "'"
1021 "\\D'l |\\n[%2]u 0'"
1022 "\\s0"
1023 "\\v'-" HALF_DOUBLE_LINE_SEP "'",
1024 span_width_reg(start_col, end_col),
1025 column_start_reg(start_col));
1026 if (!dont_move)
1027 prints("\\v'" BAR_HEIGHT "'");
1028 if (mod->stagger)
1029 prints("\\v'.5v'");
1032 void set_modifier(const entry_modifier *m)
1034 if (!m->font.empty())
1035 printfs(".ft %1\n", m->font);
1036 if (m->point_size.val != 0) {
1037 prints(".ps ");
1038 if (m->point_size.inc > 0)
1039 prints('+');
1040 else if (m->point_size.inc < 0)
1041 prints('-');
1042 printfs("%1\n", as_string(m->point_size.val));
1044 if (m->vertical_spacing.val != 0) {
1045 prints(".vs ");
1046 if (m->vertical_spacing.inc > 0)
1047 prints('+');
1048 else if (m->vertical_spacing.inc < 0)
1049 prints('-');
1050 printfs("%1\n", as_string(m->vertical_spacing.val));
1052 if (!m->macro.empty())
1053 printfs(".%1\n", m->macro);
1056 void set_inline_modifier(const entry_modifier *m)
1058 if (!m->font.empty())
1059 printfs("\\f[%1]", m->font);
1060 if (m->point_size.val != 0) {
1061 prints("\\s[");
1062 if (m->point_size.inc > 0)
1063 prints('+');
1064 else if (m->point_size.inc < 0)
1065 prints('-');
1066 printfs("%1]", as_string(m->point_size.val));
1068 if (m->stagger)
1069 prints("\\v'-.5v'");
1072 void restore_inline_modifier(const entry_modifier *m)
1074 if (!m->font.empty())
1075 prints("\\f[\\n[" SAVED_FONT_REG "]]");
1076 if (m->point_size.val != 0)
1077 prints("\\s[\\n[" SAVED_SIZE_REG "]]");
1078 if (m->stagger)
1079 prints("\\v'.5v'");
1082 class stuff
1084 public:
1085 stuff *next;
1086 int row; // occurs before row `row'
1087 char printed; // has it been printed?
1089 stuff(int);
1090 virtual void print(table *) = 0;
1091 virtual ~stuff();
1092 virtual int is_single_line() { return 0; };
1093 virtual int is_double_line() { return 0; };
1096 stuff::stuff(int r) : next(0), row(r), printed(0)
1100 stuff::~stuff()
1104 class text_stuff
1105 : public stuff
1107 public:
1108 string contents;
1109 const char *filename;
1110 int lineno;
1112 text_stuff(const string &, int, const char *, int);
1113 ~text_stuff();
1114 void print(table *);
1117 text_stuff::text_stuff(const string &s, int r, const char *fn, int ln)
1118 : stuff(r), contents(s), filename(fn), lineno(ln)
1122 text_stuff::~text_stuff()
1126 void text_stuff::print(table *)
1128 printed = 1;
1129 prints(".cp \\n(" COMPATIBLE_REG "\n");
1130 set_troff_location(filename, lineno);
1131 prints(contents);
1132 prints(".cp 0\n");
1133 location_force_filename = 1; // it might have been a .lf command
1136 class single_hline_stuff
1137 : public stuff
1139 public:
1140 single_hline_stuff(int);
1141 void print(table *);
1142 int is_single_line();
1145 single_hline_stuff::single_hline_stuff(int r) : stuff(r)
1149 void single_hline_stuff::print(table *tbl)
1151 printed = 1;
1152 tbl->print_single_hline(row);
1155 int single_hline_stuff::is_single_line()
1157 return 1;
1160 class double_hline_stuff
1161 : stuff
1163 public:
1164 double_hline_stuff(int);
1165 void print(table *);
1166 int is_double_line();
1169 double_hline_stuff::double_hline_stuff(int r) : stuff(r)
1173 void double_hline_stuff::print(table *tbl)
1175 printed = 1;
1176 tbl->print_double_hline(row);
1179 int double_hline_stuff::is_double_line()
1181 return 1;
1184 class vertical_rule
1186 public:
1187 vertical_rule *next;
1188 int start_row;
1189 int end_row;
1190 int col;
1191 char is_double;
1192 string top_adjust;
1193 string bot_adjust;
1195 vertical_rule(int, int, int, int, vertical_rule *);
1196 ~vertical_rule();
1197 void contribute_to_bottom_macro(table *);
1198 void print();
1201 vertical_rule::vertical_rule(int sr, int er, int c, int dbl,
1202 vertical_rule *p)
1203 : next(p), start_row(sr), end_row(er), col(c), is_double(dbl)
1207 vertical_rule::~vertical_rule()
1211 void vertical_rule::contribute_to_bottom_macro(table *tbl)
1213 printfs(".if \\n[" CURRENT_ROW_REG "]>=%1",
1214 as_string(start_row));
1215 if (end_row != tbl->get_nrows() - 1)
1216 printfs("&(\\n[" CURRENT_ROW_REG "]<%1)",
1217 as_string(end_row));
1218 prints(" \\{");
1219 printfs(".if %1<=\\n[" LAST_PASSED_ROW_REG "] .nr %2 \\n[#T]\n",
1220 as_string(start_row),
1221 row_top_reg(start_row));
1222 const char *offset_table[3];
1223 if (is_double) {
1224 offset_table[0] = "-" HALF_DOUBLE_LINE_SEP;
1225 offset_table[1] = "+" HALF_DOUBLE_LINE_SEP;
1226 offset_table[2] = 0;
1228 else {
1229 offset_table[0] = "";
1230 offset_table[1] = 0;
1232 for (const char **offsetp = offset_table; *offsetp; offsetp++) {
1233 prints(".sp -1\n"
1234 "\\v'" BODY_DEPTH);
1235 if (!bot_adjust.empty())
1236 printfs("+%1", bot_adjust);
1237 prints("'");
1238 printfs("\\h'\\n[%1]u%3'\\s[\\n[" LINESIZE_REG "]]\\D'l 0 |\\n[%2]u-1v",
1239 column_divide_reg(col),
1240 row_top_reg(start_row),
1241 *offsetp);
1242 if (!bot_adjust.empty())
1243 printfs("-(%1)", bot_adjust);
1244 // don't perform the top adjustment if the top is actually #T
1245 if (!top_adjust.empty())
1246 printfs("+((%1)*(%2>\\n[" LAST_PASSED_ROW_REG "]))",
1247 top_adjust,
1248 as_string(start_row));
1249 prints("'\\s0\n");
1251 prints(".\\}\n");
1254 void vertical_rule::print()
1256 printfs("\\*[" TRANSPARENT_STRING_NAME "]"
1257 ".if %1<=\\*[" QUOTE_STRING_NAME "]\\n[" LAST_PASSED_ROW_REG "] "
1258 ".nr %2 \\*[" QUOTE_STRING_NAME "]\\n[#T]\n",
1259 as_string(start_row),
1260 row_top_reg(start_row));
1261 const char *offset_table[3];
1262 if (is_double) {
1263 offset_table[0] = "-" HALF_DOUBLE_LINE_SEP;
1264 offset_table[1] = "+" HALF_DOUBLE_LINE_SEP;
1265 offset_table[2] = 0;
1267 else {
1268 offset_table[0] = "";
1269 offset_table[1] = 0;
1271 for (const char **offsetp = offset_table; *offsetp; offsetp++) {
1272 prints("\\*[" TRANSPARENT_STRING_NAME "].sp -1\n"
1273 "\\*[" TRANSPARENT_STRING_NAME "]\\v'" BODY_DEPTH);
1274 if (!bot_adjust.empty())
1275 printfs("+%1", bot_adjust);
1276 prints("'");
1277 printfs("\\h'\\n[%1]u%3'"
1278 "\\s[\\n[" LINESIZE_REG "]]"
1279 "\\D'l 0 |\\*[" QUOTE_STRING_NAME "]\\n[%2]u-1v",
1280 column_divide_reg(col),
1281 row_top_reg(start_row),
1282 *offsetp);
1283 if (!bot_adjust.empty())
1284 printfs("-(%1)", bot_adjust);
1285 // don't perform the top adjustment if the top is actually #T
1286 if (!top_adjust.empty())
1287 printfs("+((%1)*(%2>\\*[" QUOTE_STRING_NAME "]\\n["
1288 LAST_PASSED_ROW_REG "]))",
1289 top_adjust,
1290 as_string(start_row));
1291 prints("'"
1292 "\\s0\n");
1296 table::table(int nc, unsigned f, int ls, char dpc)
1297 : nrows(0), ncolumns(nc), linesize(ls), decimal_point_char(dpc),
1298 vrule_list(0), stuff_list(0), span_list(0),
1299 entry_list(0), entry_list_tailp(&entry_list), entry(0),
1300 vline(0), row_is_all_lines(0), left_separation(0), right_separation(0),
1301 total_separation(0), allocated_rows(0), flags(f)
1303 minimum_width = new string[ncolumns];
1304 column_separation = ncolumns > 1 ? new int[ncolumns - 1] : 0;
1305 equal = new char[ncolumns];
1306 expand = new char[ncolumns];
1307 int i;
1308 for (i = 0; i < ncolumns; i++) {
1309 equal[i] = 0;
1310 expand[i] = 0;
1312 for (i = 0; i < ncolumns - 1; i++)
1313 column_separation[i] = DEFAULT_COLUMN_SEPARATION;
1314 delim[0] = delim[1] = '\0';
1317 table::~table()
1319 for (int i = 0; i < nrows; i++) {
1320 a_delete entry[i];
1321 a_delete vline[i];
1323 a_delete entry;
1324 a_delete vline;
1325 while (entry_list) {
1326 table_entry *tem = entry_list;
1327 entry_list = entry_list->next;
1328 delete tem;
1330 ad_delete(ncolumns) minimum_width;
1331 a_delete column_separation;
1332 a_delete equal;
1333 a_delete expand;
1334 while (stuff_list) {
1335 stuff *tem = stuff_list;
1336 stuff_list = stuff_list->next;
1337 delete tem;
1339 while (vrule_list) {
1340 vertical_rule *tem = vrule_list;
1341 vrule_list = vrule_list->next;
1342 delete tem;
1344 a_delete row_is_all_lines;
1345 while (span_list) {
1346 horizontal_span *tem = span_list;
1347 span_list = span_list->next;
1348 delete tem;
1352 void table::set_delim(char c1, char c2)
1354 delim[0] = c1;
1355 delim[1] = c2;
1358 void table::set_minimum_width(int c, const string &w)
1360 assert(c >= 0 && c < ncolumns);
1361 minimum_width[c] = w;
1364 void table::set_column_separation(int c, int n)
1366 assert(c >= 0 && c < ncolumns - 1);
1367 column_separation[c] = n;
1370 void table::set_equal_column(int c)
1372 assert(c >= 0 && c < ncolumns);
1373 equal[c] = 1;
1376 void table::set_expand_column(int c)
1378 assert(c >= 0 && c < ncolumns);
1379 expand[c] = 1;
1382 void table::add_stuff(stuff *p)
1384 stuff **pp;
1385 for (pp = &stuff_list; *pp; pp = &(*pp)->next)
1387 *pp = p;
1390 void table::add_text_line(int r, const string &s, const char *filename,
1391 int lineno)
1393 add_stuff(new text_stuff(s, r, filename, lineno));
1396 void table::add_single_hline(int r)
1398 add_stuff(new single_hline_stuff(r));
1401 void table::add_double_hline(int r)
1403 add_stuff(new double_hline_stuff(r));
1406 void table::allocate(int r)
1408 if (r >= nrows) {
1409 typedef table_entry **PPtable_entry; // work around g++ 1.36.1 bug
1410 if (r >= allocated_rows) {
1411 if (allocated_rows == 0) {
1412 allocated_rows = 16;
1413 if (allocated_rows <= r)
1414 allocated_rows = r + 1;
1415 entry = new PPtable_entry[allocated_rows];
1416 vline = new char*[allocated_rows];
1418 else {
1419 table_entry ***old_entry = entry;
1420 int old_allocated_rows = allocated_rows;
1421 allocated_rows *= 2;
1422 if (allocated_rows <= r)
1423 allocated_rows = r + 1;
1424 entry = new PPtable_entry[allocated_rows];
1425 memcpy(entry, old_entry, sizeof(table_entry**)*old_allocated_rows);
1426 a_delete old_entry;
1427 char **old_vline = vline;
1428 vline = new char*[allocated_rows];
1429 memcpy(vline, old_vline, sizeof(char*)*old_allocated_rows);
1430 a_delete old_vline;
1433 assert(allocated_rows > r);
1434 while (nrows <= r) {
1435 entry[nrows] = new table_entry*[ncolumns];
1436 int i;
1437 for (i = 0; i < ncolumns; i++)
1438 entry[nrows][i] = 0;
1439 vline[nrows] = new char[ncolumns+1];
1440 for (i = 0; i < ncolumns+1; i++)
1441 vline[nrows][i] = 0;
1442 nrows++;
1447 void table::do_hspan(int r, int c)
1449 assert(r >= 0 && c >= 0 && r < nrows && c < ncolumns);
1450 if (c == 0) {
1451 error("first column cannot be horizontally spanned");
1452 return;
1454 table_entry *e = entry[r][c];
1455 if (e) {
1456 assert(e->start_row <= r && r <= e->end_row
1457 && e->start_col <= c && c <= e->end_col
1458 && e->end_row - e->start_row > 0
1459 && e->end_col - e->start_col > 0);
1460 return;
1462 e = entry[r][c-1];
1463 // e can be 0 if we had an empty entry or an error
1464 if (e == 0)
1465 return;
1466 if (e->start_row != r) {
1469 ^ s */
1470 error("impossible horizontal span at row %1, column %2", r + 1, c + 1);
1472 else {
1473 e->end_col = c;
1474 entry[r][c] = e;
1478 void table::do_vspan(int r, int c)
1480 assert(r >= 0 && c >= 0 && r < nrows && c < ncolumns);
1481 if (r == 0) {
1482 error("first row cannot be vertically spanned");
1483 return;
1485 table_entry *e = entry[r][c];
1486 if (e) {
1487 assert(e->start_row <= r && r <= e->end_row
1488 && e->start_col <= c && c <= e->end_col
1489 && e->end_row - e->start_row > 0
1490 && e->end_col - e->start_col > 0);
1491 return;
1493 e = entry[r-1][c];
1494 // e can be 0 if we had an empty entry or an error
1495 if (e == 0)
1496 return;
1497 if (e->start_col != c) {
1498 /* l s
1499 l ^ */
1500 error("impossible vertical span at row %1, column %2", r + 1, c + 1);
1502 else {
1503 for (int i = c; i <= e->end_col; i++) {
1504 assert(entry[r][i] == 0);
1505 entry[r][i] = e;
1507 e->end_row = r;
1511 int find_decimal_point(const char *s, char decimal_point_char,
1512 const char *delim)
1514 if (s == 0 || *s == '\0')
1515 return -1;
1516 const char *p;
1517 int in_delim = 0; // is p within eqn delimiters?
1518 // tbl recognises \& even within eqn delimiters; I don't
1519 for (p = s; *p; p++)
1520 if (in_delim) {
1521 if (*p == delim[1])
1522 in_delim = 0;
1524 else if (*p == delim[0])
1525 in_delim = 1;
1526 else if (p[0] == '\\' && p[1] == '&')
1527 return p - s;
1528 int possible_pos = -1;
1529 in_delim = 0;
1530 for (p = s; *p; p++)
1531 if (in_delim) {
1532 if (*p == delim[1])
1533 in_delim = 0;
1535 else if (*p == delim[0])
1536 in_delim = 1;
1537 else if (p[0] == decimal_point_char && csdigit(p[1]))
1538 possible_pos = p - s;
1539 if (possible_pos >= 0)
1540 return possible_pos;
1541 in_delim = 0;
1542 for (p = s; *p; p++)
1543 if (in_delim) {
1544 if (*p == delim[1])
1545 in_delim = 0;
1547 else if (*p == delim[0])
1548 in_delim = 1;
1549 else if (csdigit(*p))
1550 possible_pos = p + 1 - s;
1551 return possible_pos;
1554 void table::add_entry(int r, int c, const string &str, const entry_format *f,
1555 const char *fn, int ln)
1557 allocate(r);
1558 table_entry *e = 0;
1559 if (str == "\\_") {
1560 e = new short_line_entry(this, f);
1562 else if (str == "\\=") {
1563 e = new short_double_line_entry(this, f);
1565 else if (str == "_") {
1566 single_line_entry *lefte;
1567 if (c > 0 && entry[r][c-1] != 0 &&
1568 (lefte = entry[r][c-1]->to_single_line_entry()) != 0
1569 && lefte->start_row == r
1570 && lefte->mod->stagger == f->stagger) {
1571 lefte->end_col = c;
1572 entry[r][c] = lefte;
1574 else
1575 e = new single_line_entry(this, f);
1577 else if (str == "=") {
1578 double_line_entry *lefte;
1579 if (c > 0 && entry[r][c-1] != 0 &&
1580 (lefte = entry[r][c-1]->to_double_line_entry()) != 0
1581 && lefte->start_row == r
1582 && lefte->mod->stagger == f->stagger) {
1583 lefte->end_col = c;
1584 entry[r][c] = lefte;
1586 else
1587 e = new double_line_entry(this, f);
1589 else if (str == "\\^") {
1590 do_vspan(r, c);
1592 else if (str.length() > 2 && str[0] == '\\' && str[1] == 'R') {
1593 if (str.search('\n') >= 0)
1594 error_with_file_and_line(fn, ln, "bad repeated character");
1595 else {
1596 char *s = str.substring(2, str.length() - 2).extract();
1597 e = new repeated_char_entry(this, f, s);
1600 else {
1601 int is_block = str.search('\n') >= 0;
1602 char *s;
1603 switch (f->type) {
1604 case FORMAT_SPAN:
1605 assert(str.empty());
1606 do_hspan(r, c);
1607 break;
1608 case FORMAT_LEFT:
1609 if (!str.empty()) {
1610 s = str.extract();
1611 if (is_block)
1612 e = new left_block_entry(this, f, s);
1613 else
1614 e = new left_text_entry(this, f, s);
1616 else
1617 e = new empty_entry(this, f);
1618 break;
1619 case FORMAT_CENTER:
1620 if (!str.empty()) {
1621 s = str.extract();
1622 if (is_block)
1623 e = new center_block_entry(this, f, s);
1624 else
1625 e = new center_text_entry(this, f, s);
1627 else
1628 e = new empty_entry(this, f);
1629 break;
1630 case FORMAT_RIGHT:
1631 if (!str.empty()) {
1632 s = str.extract();
1633 if (is_block)
1634 e = new right_block_entry(this, f, s);
1635 else
1636 e = new right_text_entry(this, f, s);
1638 else
1639 e = new empty_entry(this, f);
1640 break;
1641 case FORMAT_NUMERIC:
1642 if (!str.empty()) {
1643 s = str.extract();
1644 if (is_block) {
1645 error_with_file_and_line(fn, ln, "can't have numeric text block");
1646 e = new left_block_entry(this, f, s);
1648 else {
1649 int pos = find_decimal_point(s, decimal_point_char, delim);
1650 if (pos < 0)
1651 e = new center_text_entry(this, f, s);
1652 else
1653 e = new numeric_text_entry(this, f, s, pos);
1656 else
1657 e = new empty_entry(this, f);
1658 break;
1659 case FORMAT_ALPHABETIC:
1660 if (!str.empty()) {
1661 s = str.extract();
1662 if (is_block)
1663 e = new alphabetic_block_entry(this, f, s);
1664 else
1665 e = new alphabetic_text_entry(this, f, s);
1667 else
1668 e = new empty_entry(this, f);
1669 break;
1670 case FORMAT_VSPAN:
1671 do_vspan(r, c);
1672 break;
1673 case FORMAT_HLINE:
1674 if (str.length() != 0)
1675 error_with_file_and_line(fn, ln,
1676 "non-empty data entry for `_' format ignored");
1677 e = new single_line_entry(this, f);
1678 break;
1679 case FORMAT_DOUBLE_HLINE:
1680 if (str.length() != 0)
1681 error_with_file_and_line(fn, ln,
1682 "non-empty data entry for `=' format ignored");
1683 e = new double_line_entry(this, f);
1684 break;
1685 default:
1686 assert(0);
1689 if (e) {
1690 table_entry *preve = entry[r][c];
1691 if (preve) {
1692 /* c s
1693 ^ l */
1694 error_with_file_and_line(fn, ln, "row %1, column %2 already spanned",
1695 r + 1, c + 1);
1696 delete e;
1698 else {
1699 e->input_lineno = ln;
1700 e->input_filename = fn;
1701 e->start_row = e->end_row = r;
1702 e->start_col = e->end_col = c;
1703 *entry_list_tailp = e;
1704 entry_list_tailp = &e->next;
1705 entry[r][c] = e;
1710 // add vertical lines for row r
1712 void table::add_vlines(int r, const char *v)
1714 allocate(r);
1715 for (int i = 0; i < ncolumns+1; i++)
1716 vline[r][i] = v[i];
1719 void table::check()
1721 table_entry *p = entry_list;
1722 int i, j;
1723 while (p) {
1724 for (i = p->start_row; i <= p->end_row; i++)
1725 for (j = p->start_col; j <= p->end_col; j++)
1726 assert(entry[i][j] == p);
1727 p = p->next;
1731 void table::print()
1733 location_force_filename = 1;
1734 check();
1735 init_output();
1736 determine_row_type();
1737 compute_widths();
1738 if (!(flags & CENTER))
1739 prints(".if \\n[" SAVED_CENTER_REG "] \\{");
1740 prints(".in +(u;\\n[.l]-\\n[.i]-\\n[TW]/2>?-\\n[.i])\n"
1741 ".nr " SAVED_INDENT_REG " \\n[.i]\n");
1742 if (!(flags & CENTER))
1743 prints(".\\}\n");
1744 build_vrule_list();
1745 define_bottom_macro();
1746 do_top();
1747 for (int i = 0; i < nrows; i++)
1748 do_row(i);
1749 do_bottom();
1752 void table::determine_row_type()
1754 row_is_all_lines = new char[nrows];
1755 for (int i = 0; i < nrows; i++) {
1756 int had_single = 0;
1757 int had_double = 0;
1758 int had_non_line = 0;
1759 for (int c = 0; c < ncolumns; c++) {
1760 table_entry *e = entry[i][c];
1761 if (e != 0) {
1762 if (e->start_row == e->end_row) {
1763 int t = e->line_type();
1764 switch (t) {
1765 case -1:
1766 had_non_line = 1;
1767 break;
1768 case 0:
1769 // empty
1770 break;
1771 case 1:
1772 had_single = 1;
1773 break;
1774 case 2:
1775 had_double = 1;
1776 break;
1777 default:
1778 assert(0);
1780 if (had_non_line)
1781 break;
1783 c = e->end_col;
1786 if (had_non_line)
1787 row_is_all_lines[i] = 0;
1788 else if (had_double)
1789 row_is_all_lines[i] = 2;
1790 else if (had_single)
1791 row_is_all_lines[i] = 1;
1792 else
1793 row_is_all_lines[i] = 0;
1797 int table::count_expand_columns()
1799 int count = 0;
1800 for (int i = 0; i < ncolumns; i++)
1801 if (expand[i])
1802 count++;
1803 return count;
1806 void table::init_output()
1808 prints(".nr " COMPATIBLE_REG " \\n(.C\n"
1809 ".cp 0\n");
1810 if (linesize > 0)
1811 printfs(".nr " LINESIZE_REG " %1\n", as_string(linesize));
1812 else
1813 prints(".nr " LINESIZE_REG " \\n[.s]\n");
1814 if (!(flags & CENTER))
1815 prints(".nr " SAVED_CENTER_REG " \\n[.ce]\n");
1816 if (compatible_flag)
1817 prints(".ds " LEADER_REG " \\a\n");
1818 prints(".de " RESET_MACRO_NAME "\n"
1819 ".ft \\n[.f]\n"
1820 ".ps \\n[.s]\n"
1821 ".vs \\n[.v]u\n"
1822 ".in \\n[.i]u\n"
1823 ".ll \\n[.l]u\n"
1824 ".ls \\n[.L]\n"
1825 ".ad \\n[.j]\n"
1826 ".ie \\n[.u] .fi\n"
1827 ".el .nf\n"
1828 ".ce \\n[.ce]\n"
1829 "..\n"
1830 ".nr " SAVED_INDENT_REG " \\n[.i]\n"
1831 ".nr " SAVED_FONT_REG " \\n[.f]\n"
1832 ".nr " SAVED_SIZE_REG " \\n[.s]\n"
1833 ".nr " SAVED_FILL_REG " \\n[.u]\n"
1834 ".nr T. 0\n"
1835 ".nr " CURRENT_ROW_REG " 0-1\n"
1836 ".nr " LAST_PASSED_ROW_REG " 0-1\n"
1837 ".nr " SECTION_DIVERSION_FLAG_REG " 0\n"
1838 ".ds " TRANSPARENT_STRING_NAME "\n"
1839 ".ds " QUOTE_STRING_NAME "\n"
1840 ".nr " NEED_BOTTOM_RULE_REG " 1\n"
1841 ".nr " SUPPRESS_BOTTOM_REG " 0\n"
1842 ".eo\n"
1843 ".de " REPEATED_MARK_MACRO "\n"
1844 ".mk \\$1\n"
1845 ".if !'\\n(.z'' \\!." REPEATED_MARK_MACRO " \"\\$1\"\n"
1846 "..\n"
1847 ".de " REPEATED_VPT_MACRO "\n"
1848 ".vpt \\$1\n"
1849 ".if !'\\n(.z'' \\!." REPEATED_VPT_MACRO " \"\\$1\"\n"
1850 "..\n");
1851 if (!(flags & NOKEEP))
1852 prints(".de " KEEP_MACRO_NAME "\n"
1853 ".if '\\n[.z]'' \\{.ds " QUOTE_STRING_NAME " \\\\\n"
1854 ".ds " TRANSPARENT_STRING_NAME " \\!\n"
1855 ".di " SECTION_DIVERSION_NAME "\n"
1856 ".nr " SECTION_DIVERSION_FLAG_REG " 1\n"
1857 ".in 0\n"
1858 ".\\}\n"
1859 "..\n"
1860 ".de " RELEASE_MACRO_NAME "\n"
1861 ".if \\n[" SECTION_DIVERSION_FLAG_REG "] \\{"
1862 ".di\n"
1863 ".in \\n[" SAVED_INDENT_REG "]u\n"
1864 ".nr " SAVED_DN_REG " \\n[dn]\n"
1865 ".ds " QUOTE_STRING_NAME "\n"
1866 ".ds " TRANSPARENT_STRING_NAME "\n"
1867 ".nr " SECTION_DIVERSION_FLAG_REG " 0\n"
1868 ".if \\n[.t]<=\\n[dn] \\{"
1869 ".nr T. 1\n"
1870 ".T#\n"
1871 ".nr " SUPPRESS_BOTTOM_REG " 1\n"
1872 ".sp \\n[.t]u\n"
1873 ".nr " SUPPRESS_BOTTOM_REG " 0\n"
1874 ".mk #T\n"
1875 ".\\}\n"
1876 ".if \\n[.t]<=\\n[" SAVED_DN_REG "] "
1877 /* Since we turn off traps, it won't get into an infinite loop
1878 when we try and print it; it will just go off the bottom of the
1879 page. */
1880 ".tm warning: page \\n%: table text block will not fit on one page\n"
1881 ".nf\n"
1882 ".ls 1\n"
1883 "." SECTION_DIVERSION_NAME "\n"
1884 ".ls\n"
1885 ".rm " SECTION_DIVERSION_NAME "\n"
1886 ".\\}\n"
1887 "..\n"
1888 ".nr " TABLE_DIVERSION_FLAG_REG " 0\n"
1889 ".de " TABLE_KEEP_MACRO_NAME "\n"
1890 ".if '\\n[.z]'' \\{"
1891 ".di " TABLE_DIVERSION_NAME "\n"
1892 ".nr " TABLE_DIVERSION_FLAG_REG " 1\n"
1893 ".\\}\n"
1894 "..\n"
1895 ".de " TABLE_RELEASE_MACRO_NAME "\n"
1896 ".if \\n[" TABLE_DIVERSION_FLAG_REG "] \\{.br\n"
1897 ".di\n"
1898 ".nr " SAVED_DN_REG " \\n[dn]\n"
1899 ".ne \\n[dn]u+\\n[.V]u\n"
1900 ".ie \\n[.t]<=\\n[" SAVED_DN_REG "] "
1901 ".tm error: page \\n%: table will not fit on one page; use .TS H/.TH with a supporting macro package\n"
1902 ".el \\{"
1903 ".in 0\n"
1904 ".ls 1\n"
1905 ".nf\n"
1906 "." TABLE_DIVERSION_NAME "\n"
1907 ".\\}\n"
1908 ".rm " TABLE_DIVERSION_NAME "\n"
1909 ".\\}\n"
1910 "..\n");
1911 prints(".ec\n"
1912 ".ce 0\n"
1913 ".nf\n");
1916 string block_width_reg(int r, int c)
1918 static char name[sizeof(BLOCK_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1919 sprintf(name, BLOCK_WIDTH_PREFIX "%d,%d", r, c);
1920 return string(name);
1923 string block_diversion_name(int r, int c)
1925 static char name[sizeof(BLOCK_DIVERSION_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1926 sprintf(name, BLOCK_DIVERSION_PREFIX "%d,%d", r, c);
1927 return string(name);
1930 string block_height_reg(int r, int c)
1932 static char name[sizeof(BLOCK_HEIGHT_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1933 sprintf(name, BLOCK_HEIGHT_PREFIX "%d,%d", r, c);
1934 return string(name);
1937 string span_width_reg(int start_col, int end_col)
1939 static char name[sizeof(SPAN_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1940 sprintf(name, SPAN_WIDTH_PREFIX "%d", start_col);
1941 if (end_col != start_col)
1942 sprintf(strchr(name, '\0'), ",%d", end_col);
1943 return string(name);
1946 string span_left_numeric_width_reg(int start_col, int end_col)
1948 static char name[sizeof(SPAN_LEFT_NUMERIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1949 sprintf(name, SPAN_LEFT_NUMERIC_WIDTH_PREFIX "%d", start_col);
1950 if (end_col != start_col)
1951 sprintf(strchr(name, '\0'), ",%d", end_col);
1952 return string(name);
1955 string span_right_numeric_width_reg(int start_col, int end_col)
1957 static char name[sizeof(SPAN_RIGHT_NUMERIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1958 sprintf(name, SPAN_RIGHT_NUMERIC_WIDTH_PREFIX "%d", start_col);
1959 if (end_col != start_col)
1960 sprintf(strchr(name, '\0'), ",%d", end_col);
1961 return string(name);
1964 string span_alphabetic_width_reg(int start_col, int end_col)
1966 static char name[sizeof(SPAN_ALPHABETIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1967 sprintf(name, SPAN_ALPHABETIC_WIDTH_PREFIX "%d", start_col);
1968 if (end_col != start_col)
1969 sprintf(strchr(name, '\0'), ",%d", end_col);
1970 return string(name);
1973 string column_separation_reg(int col)
1975 static char name[sizeof(COLUMN_SEPARATION_PREFIX)+INT_DIGITS];
1976 sprintf(name, COLUMN_SEPARATION_PREFIX "%d", col);
1977 return string(name);
1980 string row_start_reg(int row)
1982 static char name[sizeof(ROW_START_PREFIX)+INT_DIGITS];
1983 sprintf(name, ROW_START_PREFIX "%d", row);
1984 return string(name);
1987 string column_start_reg(int col)
1989 static char name[sizeof(COLUMN_START_PREFIX)+INT_DIGITS];
1990 sprintf(name, COLUMN_START_PREFIX "%d", col);
1991 return string(name);
1994 string column_end_reg(int col)
1996 static char name[sizeof(COLUMN_END_PREFIX)+INT_DIGITS];
1997 sprintf(name, COLUMN_END_PREFIX "%d", col);
1998 return string(name);
2001 string column_divide_reg(int col)
2003 static char name[sizeof(COLUMN_DIVIDE_PREFIX)+INT_DIGITS];
2004 sprintf(name, COLUMN_DIVIDE_PREFIX "%d", col);
2005 return string(name);
2008 string row_top_reg(int row)
2010 static char name[sizeof(ROW_TOP_PREFIX)+INT_DIGITS];
2011 sprintf(name, ROW_TOP_PREFIX "%d", row);
2012 return string(name);
2015 void init_span_reg(int start_col, int end_col)
2017 printfs(".nr %1 \\n(.H\n.nr %2 0\n.nr %3 0\n.nr %4 0\n",
2018 span_width_reg(start_col, end_col),
2019 span_alphabetic_width_reg(start_col, end_col),
2020 span_left_numeric_width_reg(start_col, end_col),
2021 span_right_numeric_width_reg(start_col, end_col));
2024 void compute_span_width(int start_col, int end_col)
2026 printfs(".nr %1 \\n[%1]>?(\\n[%2]+\\n[%3])\n"
2027 ".if \\n[%4] .nr %1 \\n[%1]>?(\\n[%4]+2n)\n",
2028 span_width_reg(start_col, end_col),
2029 span_left_numeric_width_reg(start_col, end_col),
2030 span_right_numeric_width_reg(start_col, end_col),
2031 span_alphabetic_width_reg(start_col, end_col));
2034 // Increase the widths of columns so that the width of any spanning entry
2035 // is not greater than the sum of the widths of the columns that it spans.
2036 // Ensure that the widths of columns remain equal.
2038 void table::divide_span(int start_col, int end_col)
2040 assert(end_col > start_col);
2041 printfs(".nr " NEEDED_REG " \\n[%1]-(\\n[%2]",
2042 span_width_reg(start_col, end_col),
2043 span_width_reg(start_col, start_col));
2044 int i;
2045 for (i = start_col + 1; i <= end_col; i++) {
2046 // The column separation may shrink with the expand option.
2047 if (!(flags & EXPAND))
2048 printfs("+%1n", as_string(column_separation[i - 1]));
2049 printfs("+\\n[%1]", span_width_reg(i, i));
2051 prints(")\n");
2052 printfs(".nr " NEEDED_REG " \\n[" NEEDED_REG "]/%1\n",
2053 as_string(end_col - start_col + 1));
2054 prints(".if \\n[" NEEDED_REG "] \\{");
2055 for (i = start_col; i <= end_col; i++)
2056 printfs(".nr %1 +\\n[" NEEDED_REG "]\n",
2057 span_width_reg(i, i));
2058 int equal_flag = 0;
2059 for (i = start_col; i <= end_col && !equal_flag; i++)
2060 if (equal[i] || expand[i])
2061 equal_flag = 1;
2062 if (equal_flag) {
2063 for (i = 0; i < ncolumns; i++)
2064 if (i < start_col || i > end_col)
2065 printfs(".nr %1 +\\n[" NEEDED_REG "]\n",
2066 span_width_reg(i, i));
2068 prints(".\\}\n");
2071 void table::sum_columns(int start_col, int end_col, int do_expand)
2073 assert(end_col > start_col);
2074 int i;
2075 for (i = start_col; i <= end_col; i++)
2076 if (expand[i])
2077 break;
2078 if (i > end_col) {
2079 if (do_expand)
2080 return;
2082 else {
2083 if (!do_expand)
2084 return;
2086 printfs(".nr %1 \\n[%2]",
2087 span_width_reg(start_col, end_col),
2088 span_width_reg(start_col, start_col));
2089 for (i = start_col + 1; i <= end_col; i++)
2090 printfs("+(%1*\\n[" SEPARATION_FACTOR_REG "])+\\n[%2]",
2091 as_string(column_separation[i - 1]),
2092 span_width_reg(i, i));
2093 prints('\n');
2096 horizontal_span::horizontal_span(int sc, int ec, horizontal_span *p)
2097 : next(p), start_col(sc), end_col(ec)
2101 void table::build_span_list()
2103 span_list = 0;
2104 table_entry *p = entry_list;
2105 while (p) {
2106 if (p->end_col != p->start_col) {
2107 horizontal_span *q;
2108 for (q = span_list; q; q = q->next)
2109 if (q->start_col == p->start_col
2110 && q->end_col == p->end_col)
2111 break;
2112 if (!q)
2113 span_list = new horizontal_span(p->start_col, p->end_col, span_list);
2115 p = p->next;
2117 // Now sort span_list primarily by order of end_row, and secondarily
2118 // by reverse order of start_row. This ensures that if we divide
2119 // spans using the order in span_list, we will get reasonable results.
2120 horizontal_span *unsorted = span_list;
2121 span_list = 0;
2122 while (unsorted) {
2123 horizontal_span **pp;
2124 for (pp = &span_list; *pp; pp = &(*pp)->next)
2125 if (unsorted->end_col < (*pp)->end_col
2126 || (unsorted->end_col == (*pp)->end_col
2127 && (unsorted->start_col > (*pp)->start_col)))
2128 break;
2129 horizontal_span *tem = unsorted->next;
2130 unsorted->next = *pp;
2131 *pp = unsorted;
2132 unsorted = tem;
2136 void table::compute_expand_width()
2138 int i;
2139 int colcount = count_expand_columns();
2140 prints(".nr " EXPAND_REG " \\n[.l]-\\n[.i]");
2141 for (i = 0; i < ncolumns; i++)
2142 if (!expand[i])
2143 printfs("-\\n[%1]", span_width_reg(i, i));
2144 if (total_separation)
2145 printfs("-%1n", as_string(total_separation));
2146 prints("\n");
2147 prints(".if \\n[" EXPAND_REG "]<0 \\{");
2148 entry_list->set_location();
2149 if (!(flags & NOWARN))
2150 prints(
2151 ". tm warning: file \"\\n[.F]\", near line \\n[.c]:\n"
2152 ". tm1 \" table wider than line width\n");
2153 prints(
2154 ". nr " EXPAND_REG " 0\n"
2155 ".\\}\n");
2156 if (colcount > 1)
2157 printfs(".nr " EXPAND_REG " \\n[" EXPAND_REG "]/%1\n",
2158 as_string(colcount));
2159 for (i = 0; i < ncolumns; i++)
2160 if (expand[i])
2161 printfs(".nr %1 \\n[%1]>?\\n[" EXPAND_REG "]\n", span_width_reg(i, i));
2164 void table::compute_total_separation()
2166 if (flags & (ALLBOX | BOX | DOUBLEBOX))
2167 left_separation = right_separation = 1;
2168 else {
2169 for (int i = 0; i < nrows; i++) {
2170 if (vline[i][0] > 0)
2171 left_separation = 1;
2172 if (vline[i][ncolumns] > 0)
2173 right_separation = 1;
2176 total_separation = left_separation + right_separation;
2177 int i;
2178 for (i = 0; i < ncolumns - 1; i++)
2179 total_separation += column_separation[i];
2182 void table::compute_separation_factor()
2184 // Don't let the separation factor be negative.
2185 prints(".nr " SEPARATION_FACTOR_REG " \\n[.l]-\\n[.i]");
2186 for (int i = 0; i < ncolumns; i++)
2187 printfs("-\\n[%1]", span_width_reg(i, i));
2188 printfs("/%1\n", as_string(total_separation));
2189 prints(".ie \\n[" SEPARATION_FACTOR_REG "]<=0 \\{");
2190 entry_list->set_location();
2191 if (!(flags & NOWARN))
2192 prints(
2193 ". tm warning: file \"\\n[.F]\", around line \\n[.c]:\n"
2194 ". tm1 \" column separation set to zero\n");
2195 prints(". nr " SEPARATION_FACTOR_REG " 0\n"
2196 ".\\}\n"
2197 ".el .if \\n[" SEPARATION_FACTOR_REG "]<1n \\{");
2198 entry_list->set_location();
2199 if (!(flags & NOWARN))
2200 prints(
2201 ". tm warning: file \"\\n[.F]\", around line \\n[.c]:\n"
2202 ". tm1 \" table squeezed horizontally to fit line length\n");
2203 prints(".\\}\n");
2206 void table::compute_column_positions()
2208 printfs(".nr %1 0\n", column_divide_reg(0));
2209 printfs(".nr %1 %2*\\n[" SEPARATION_FACTOR_REG "]\n",
2210 column_start_reg(0),
2211 as_string(left_separation));
2212 int i;
2213 for (i = 1;; i++) {
2214 printfs(".nr %1 \\n[%2]+\\n[%3]\n",
2215 column_end_reg(i-1),
2216 column_start_reg(i-1),
2217 span_width_reg(i-1, i-1));
2218 if (i >= ncolumns)
2219 break;
2220 printfs(".nr %1 \\n[%2]+(%3*\\n[" SEPARATION_FACTOR_REG "])\n",
2221 column_start_reg(i),
2222 column_end_reg(i-1),
2223 as_string(column_separation[i-1]));
2224 printfs(".nr %1 \\n[%2]+\\n[%3]/2\n",
2225 column_divide_reg(i),
2226 column_end_reg(i-1),
2227 column_start_reg(i));
2229 printfs(".nr %1 \\n[%2]+(%3*\\n[" SEPARATION_FACTOR_REG "])\n",
2230 column_divide_reg(ncolumns),
2231 column_end_reg(i-1),
2232 as_string(right_separation));
2233 printfs(".nr TW \\n[%1]\n",
2234 column_divide_reg(ncolumns));
2235 if (flags & DOUBLEBOX) {
2236 printfs(".nr %1 +" DOUBLE_LINE_SEP "\n", column_divide_reg(0));
2237 printfs(".nr %1 -" DOUBLE_LINE_SEP "\n", column_divide_reg(ncolumns));
2241 void table::make_columns_equal()
2243 int first = -1; // index of first equal column
2244 int i;
2245 for (i = 0; i < ncolumns; i++)
2246 if (equal[i]) {
2247 if (first < 0) {
2248 printfs(".nr %1 \\n[%1]", span_width_reg(i, i));
2249 first = i;
2251 else
2252 printfs(">?\\n[%1]", span_width_reg(i, i));
2254 if (first >= 0) {
2255 prints('\n');
2256 for (i = first + 1; i < ncolumns; i++)
2257 if (equal[i])
2258 printfs(".nr %1 \\n[%2]\n",
2259 span_width_reg(i, i),
2260 span_width_reg(first, first));
2264 void table::compute_widths()
2266 build_span_list();
2267 int i;
2268 horizontal_span *p;
2269 // These values get refined later.
2270 prints(".nr " SEPARATION_FACTOR_REG " 1n\n");
2271 for (i = 0; i < ncolumns; i++) {
2272 init_span_reg(i, i);
2273 if (!minimum_width[i].empty())
2274 printfs(".nr %1 (n;%2)\n", span_width_reg(i, i), minimum_width[i]);
2276 for (p = span_list; p; p = p->next)
2277 init_span_reg(p->start_col, p->end_col);
2278 // Compute all field widths except for blocks.
2279 table_entry *q;
2280 for (q = entry_list; q; q = q->next)
2281 if (!q->mod->zero_width)
2282 q->do_width();
2283 // Compute all span widths, not handling blocks yet.
2284 for (i = 0; i < ncolumns; i++)
2285 compute_span_width(i, i);
2286 for (p = span_list; p; p = p->next)
2287 compute_span_width(p->start_col, p->end_col);
2288 // Making columns equal normally increases the width of some columns.
2289 make_columns_equal();
2290 // Note that divide_span keeps equal width columns equal.
2291 // This function might increase the width of some columns, too.
2292 for (p = span_list; p; p = p->next)
2293 divide_span(p->start_col, p->end_col);
2294 compute_total_separation();
2295 for (p = span_list; p; p = p->next)
2296 sum_columns(p->start_col, p->end_col, 0);
2297 // Now handle unexpanded blocks.
2298 int had_spanning_block = 0;
2299 int had_equal_block = 0;
2300 for (q = entry_list; q; q = q->next)
2301 if (q->divert(ncolumns, minimum_width,
2302 (flags & EXPAND) ? column_separation : 0, 0)) {
2303 if (q->end_col > q->start_col)
2304 had_spanning_block = 1;
2305 for (i = q->start_col; i <= q->end_col && !had_equal_block; i++)
2306 if (equal[i])
2307 had_equal_block = 1;
2309 // Adjust widths.
2310 if (had_equal_block)
2311 make_columns_equal();
2312 if (had_spanning_block)
2313 for (p = span_list; p; p = p->next)
2314 divide_span(p->start_col, p->end_col);
2315 compute_expand_width();
2316 if ((flags & EXPAND) && total_separation != 0) {
2317 compute_separation_factor();
2318 for (p = span_list; p; p = p->next)
2319 sum_columns(p->start_col, p->end_col, 0);
2321 else {
2322 // Handle expanded blocks.
2323 for (p = span_list; p; p = p->next)
2324 sum_columns(p->start_col, p->end_col, 1);
2325 for (q = entry_list; q; q = q->next)
2326 if (q->divert(ncolumns, minimum_width, 0, 1)) {
2327 if (q->end_col > q->start_col)
2328 had_spanning_block = 1;
2330 // Adjust widths again.
2331 if (had_spanning_block)
2332 for (p = span_list; p; p = p->next)
2333 divide_span(p->start_col, p->end_col);
2335 compute_column_positions();
2338 void table::print_single_hline(int r)
2340 prints(".vs " LINE_SEP ">?\\n[.V]u\n"
2341 ".ls 1\n"
2342 "\\v'" BODY_DEPTH "'"
2343 "\\s[\\n[" LINESIZE_REG "]]");
2344 if (r > nrows - 1)
2345 prints("\\D'l |\\n[TW]u 0'");
2346 else {
2347 int start_col = 0;
2348 for (;;) {
2349 while (start_col < ncolumns
2350 && entry[r][start_col] != 0
2351 && entry[r][start_col]->start_row != r)
2352 start_col++;
2353 int end_col;
2354 for (end_col = start_col;
2355 end_col < ncolumns
2356 && (entry[r][end_col] == 0
2357 || entry[r][end_col]->start_row == r);
2358 end_col++)
2360 if (end_col <= start_col)
2361 break;
2362 printfs("\\h'|\\n[%1]u",
2363 column_divide_reg(start_col));
2364 if ((r > 0 && vline[r-1][start_col] == 2)
2365 || (r < nrows && vline[r][start_col] == 2))
2366 prints("-" HALF_DOUBLE_LINE_SEP);
2367 prints("'");
2368 printfs("\\D'l |\\n[%1]u",
2369 column_divide_reg(end_col));
2370 if ((r > 0 && vline[r-1][end_col] == 2)
2371 || (r < nrows && vline[r][end_col] == 2))
2372 prints("+" HALF_DOUBLE_LINE_SEP);
2373 prints(" 0'");
2374 start_col = end_col;
2377 prints("\\s0\n");
2378 prints(".ls\n"
2379 ".vs\n");
2382 void table::print_double_hline(int r)
2384 prints(".vs " LINE_SEP "+" DOUBLE_LINE_SEP
2385 ">?\\n[.V]u\n"
2386 ".ls 1\n"
2387 "\\v'" BODY_DEPTH "'"
2388 "\\s[\\n[" LINESIZE_REG "]]");
2389 if (r > nrows - 1)
2390 prints("\\v'-" DOUBLE_LINE_SEP "'"
2391 "\\D'l |\\n[TW]u 0'"
2392 "\\v'" DOUBLE_LINE_SEP "'"
2393 "\\h'|0'"
2394 "\\D'l |\\n[TW]u 0'");
2395 else {
2396 int start_col = 0;
2397 for (;;) {
2398 while (start_col < ncolumns
2399 && entry[r][start_col] != 0
2400 && entry[r][start_col]->start_row != r)
2401 start_col++;
2402 int end_col;
2403 for (end_col = start_col;
2404 end_col < ncolumns
2405 && (entry[r][end_col] == 0
2406 || entry[r][end_col]->start_row == r);
2407 end_col++)
2409 if (end_col <= start_col)
2410 break;
2411 const char *left_adjust = 0;
2412 if ((r > 0 && vline[r-1][start_col] == 2)
2413 || (r < nrows && vline[r][start_col] == 2))
2414 left_adjust = "-" HALF_DOUBLE_LINE_SEP;
2415 const char *right_adjust = 0;
2416 if ((r > 0 && vline[r-1][end_col] == 2)
2417 || (r < nrows && vline[r][end_col] == 2))
2418 right_adjust = "+" HALF_DOUBLE_LINE_SEP;
2419 printfs("\\v'-" DOUBLE_LINE_SEP "'"
2420 "\\h'|\\n[%1]u",
2421 column_divide_reg(start_col));
2422 if (left_adjust)
2423 prints(left_adjust);
2424 prints("'");
2425 printfs("\\D'l |\\n[%1]u",
2426 column_divide_reg(end_col));
2427 if (right_adjust)
2428 prints(right_adjust);
2429 prints(" 0'");
2430 printfs("\\v'" DOUBLE_LINE_SEP "'"
2431 "\\h'|\\n[%1]u",
2432 column_divide_reg(start_col));
2433 if (left_adjust)
2434 prints(left_adjust);
2435 prints("'");
2436 printfs("\\D'l |\\n[%1]u",
2437 column_divide_reg(end_col));
2438 if (right_adjust)
2439 prints(right_adjust);
2440 prints(" 0'");
2441 start_col = end_col;
2444 prints("\\s0\n"
2445 ".ls\n"
2446 ".vs\n");
2449 void table::compute_vrule_top_adjust(int start_row, int col, string &result)
2451 if (row_is_all_lines[start_row] && start_row < nrows - 1) {
2452 if (row_is_all_lines[start_row] == 2)
2453 result = LINE_SEP ">?\\n[.V]u" "+" DOUBLE_LINE_SEP;
2454 else
2455 result = LINE_SEP ">?\\n[.V]u";
2456 start_row++;
2458 else {
2459 result = "";
2460 if (start_row == 0)
2461 return;
2462 for (stuff *p = stuff_list; p && p->row <= start_row; p = p->next)
2463 if (p->row == start_row
2464 && (p->is_single_line() || p->is_double_line()))
2465 return;
2467 int left = 0;
2468 if (col > 0) {
2469 table_entry *e = entry[start_row-1][col-1];
2470 if (e && e->start_row == e->end_row) {
2471 if (e->to_double_line_entry() != 0)
2472 left = 2;
2473 else if (e->to_single_line_entry() != 0)
2474 left = 1;
2477 int right = 0;
2478 if (col < ncolumns) {
2479 table_entry *e = entry[start_row-1][col];
2480 if (e && e->start_row == e->end_row) {
2481 if (e->to_double_line_entry() != 0)
2482 right = 2;
2483 else if (e->to_single_line_entry() != 0)
2484 right = 1;
2487 if (row_is_all_lines[start_row-1] == 0) {
2488 if (left > 0 || right > 0) {
2489 result += "-" BODY_DEPTH "-" BAR_HEIGHT;
2490 if ((left == 2 && right != 2) || (right == 2 && left != 2))
2491 result += "-" HALF_DOUBLE_LINE_SEP;
2492 else if (left == 2 && right == 2)
2493 result += "+" HALF_DOUBLE_LINE_SEP;
2496 else if (row_is_all_lines[start_row-1] == 2) {
2497 if ((left == 2 && right != 2) || (right == 2 && left != 2))
2498 result += "-" DOUBLE_LINE_SEP;
2499 else if (left == 1 || right == 1)
2500 result += "-" HALF_DOUBLE_LINE_SEP;
2504 void table::compute_vrule_bot_adjust(int end_row, int col, string &result)
2506 if (row_is_all_lines[end_row] && end_row > 0) {
2507 end_row--;
2508 result = "";
2510 else {
2511 stuff *p;
2512 for (p = stuff_list; p && p->row < end_row + 1; p = p->next)
2514 if (p && p->row == end_row + 1 && p->is_double_line()) {
2515 result = "-" DOUBLE_LINE_SEP;
2516 return;
2518 if ((p != 0 && p->row == end_row + 1)
2519 || end_row == nrows - 1) {
2520 result = "";
2521 return;
2523 if (row_is_all_lines[end_row+1] == 1)
2524 result = LINE_SEP;
2525 else if (row_is_all_lines[end_row+1] == 2)
2526 result = LINE_SEP "+" DOUBLE_LINE_SEP;
2527 else
2528 result = "";
2530 int left = 0;
2531 if (col > 0) {
2532 table_entry *e = entry[end_row+1][col-1];
2533 if (e && e->start_row == e->end_row) {
2534 if (e->to_double_line_entry() != 0)
2535 left = 2;
2536 else if (e->to_single_line_entry() != 0)
2537 left = 1;
2540 int right = 0;
2541 if (col < ncolumns) {
2542 table_entry *e = entry[end_row+1][col];
2543 if (e && e->start_row == e->end_row) {
2544 if (e->to_double_line_entry() != 0)
2545 right = 2;
2546 else if (e->to_single_line_entry() != 0)
2547 right = 1;
2550 if (row_is_all_lines[end_row+1] == 0) {
2551 if (left > 0 || right > 0) {
2552 result = "1v-" BODY_DEPTH "-" BAR_HEIGHT;
2553 if ((left == 2 && right != 2) || (right == 2 && left != 2))
2554 result += "+" HALF_DOUBLE_LINE_SEP;
2555 else if (left == 2 && right == 2)
2556 result += "-" HALF_DOUBLE_LINE_SEP;
2559 else if (row_is_all_lines[end_row+1] == 2) {
2560 if (left == 2 && right == 2)
2561 result += "-" DOUBLE_LINE_SEP;
2562 else if (left != 2 && right != 2 && (left == 1 || right == 1))
2563 result += "-" HALF_DOUBLE_LINE_SEP;
2567 void table::add_vertical_rule(int start_row, int end_row,
2568 int col, int is_double)
2570 vrule_list = new vertical_rule(start_row, end_row, col, is_double,
2571 vrule_list);
2572 compute_vrule_top_adjust(start_row, col, vrule_list->top_adjust);
2573 compute_vrule_bot_adjust(end_row, col, vrule_list->bot_adjust);
2576 void table::build_vrule_list()
2578 int col;
2579 if (flags & ALLBOX) {
2580 for (col = 1; col < ncolumns; col++) {
2581 int start_row = 0;
2582 for (;;) {
2583 while (start_row < nrows && vline_spanned(start_row, col))
2584 start_row++;
2585 if (start_row >= nrows)
2586 break;
2587 int end_row = start_row;
2588 while (end_row < nrows && !vline_spanned(end_row, col))
2589 end_row++;
2590 end_row--;
2591 add_vertical_rule(start_row, end_row, col, 0);
2592 start_row = end_row + 1;
2596 if (flags & (BOX | ALLBOX | DOUBLEBOX)) {
2597 add_vertical_rule(0, nrows - 1, 0, 0);
2598 add_vertical_rule(0, nrows - 1, ncolumns, 0);
2600 for (int end_row = 0; end_row < nrows; end_row++)
2601 for (col = 0; col < ncolumns+1; col++)
2602 if (vline[end_row][col] > 0
2603 && !vline_spanned(end_row, col)
2604 && (end_row == nrows - 1
2605 || vline[end_row+1][col] != vline[end_row][col]
2606 || vline_spanned(end_row+1, col))) {
2607 int start_row;
2608 for (start_row = end_row - 1;
2609 start_row >= 0
2610 && vline[start_row][col] == vline[end_row][col]
2611 && !vline_spanned(start_row, col);
2612 start_row--)
2614 start_row++;
2615 add_vertical_rule(start_row, end_row, col, vline[end_row][col] > 1);
2617 for (vertical_rule *p = vrule_list; p; p = p->next)
2618 if (p->is_double)
2619 for (int r = p->start_row; r <= p->end_row; r++) {
2620 if (p->col > 0 && entry[r][p->col-1] != 0
2621 && entry[r][p->col-1]->end_col == p->col-1) {
2622 int is_corner = r == p->start_row || r == p->end_row;
2623 entry[r][p->col-1]->note_double_vrule_on_right(is_corner);
2625 if (p->col < ncolumns && entry[r][p->col] != 0
2626 && entry[r][p->col]->start_col == p->col) {
2627 int is_corner = r == p->start_row || r == p->end_row;
2628 entry[r][p->col]->note_double_vrule_on_left(is_corner);
2633 void table::define_bottom_macro()
2635 prints(".eo\n"
2636 ".de T#\n"
2637 ".if !\\n[" SUPPRESS_BOTTOM_REG "] \\{"
2638 "." REPEATED_VPT_MACRO " 0\n"
2639 ".mk " SAVED_VERTICAL_POS_REG "\n");
2640 if (flags & (BOX | ALLBOX | DOUBLEBOX)) {
2641 prints(".if \\n[T.]&\\n[" NEED_BOTTOM_RULE_REG "] \\{");
2642 print_single_hline(0);
2643 prints(".\\}\n");
2645 prints(".ls 1\n");
2646 for (vertical_rule *p = vrule_list; p; p = p->next)
2647 p->contribute_to_bottom_macro(this);
2648 if (flags & DOUBLEBOX)
2649 prints(".if \\n[T.] \\{.vs " DOUBLE_LINE_SEP ">?\\n[.V]u\n"
2650 "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]"
2651 "\\D'l \\n[TW]u 0'\\s0\n"
2652 ".vs\n"
2653 ".\\}\n"
2654 ".if \\n[" LAST_PASSED_ROW_REG "]>=0 "
2655 ".nr " TOP_REG " \\n[#T]-" DOUBLE_LINE_SEP "\n"
2656 ".sp -1\n"
2657 "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]"
2658 "\\D'l 0 |\\n[" TOP_REG "]u-1v'\\s0\n"
2659 ".sp -1\n"
2660 "\\v'" BODY_DEPTH "'\\h'|\\n[TW]u'\\s[\\n[" LINESIZE_REG "]]"
2661 "\\D'l 0 |\\n[" TOP_REG "]u-1v'\\s0\n");
2662 prints(".ls\n");
2663 prints(".nr " LAST_PASSED_ROW_REG " \\n[" CURRENT_ROW_REG "]\n"
2664 ".sp |\\n[" SAVED_VERTICAL_POS_REG "]u\n"
2665 "." REPEATED_VPT_MACRO " 1\n"
2666 ".\\}\n"
2667 "..\n"
2668 ".ec\n");
2671 // is the vertical line before column c in row r horizontally spanned?
2673 int table::vline_spanned(int r, int c)
2675 assert(r >= 0 && r < nrows && c >= 0 && c < ncolumns + 1);
2676 return (c != 0 && c != ncolumns && entry[r][c] != 0
2677 && entry[r][c]->start_col != c
2678 // horizontally spanning lines don't count
2679 && entry[r][c]->to_double_line_entry() == 0
2680 && entry[r][c]->to_single_line_entry() == 0);
2683 int table::row_begins_section(int r)
2685 assert(r >= 0 && r < nrows);
2686 for (int i = 0; i < ncolumns; i++)
2687 if (entry[r][i] && entry[r][i]->start_row != r)
2688 return 0;
2689 return 1;
2692 int table::row_ends_section(int r)
2694 assert(r >= 0 && r < nrows);
2695 for (int i = 0; i < ncolumns; i++)
2696 if (entry[r][i] && entry[r][i]->end_row != r)
2697 return 0;
2698 return 1;
2701 void table::do_row(int r)
2703 if (!(flags & NOKEEP) && row_begins_section(r))
2704 prints("." KEEP_MACRO_NAME "\n");
2705 int had_line = 0;
2706 stuff *p;
2707 for (p = stuff_list; p && p->row < r; p = p->next)
2709 for (stuff *p1 = p; p1 && p1->row == r; p1 = p1->next)
2710 if (!p1->printed && (p1->is_single_line() || p1->is_double_line())) {
2711 had_line = 1;
2712 break;
2714 if (!had_line && !row_is_all_lines[r])
2715 printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r));
2716 had_line = 0;
2717 for (; p && p->row == r; p = p->next)
2718 if (!p->printed) {
2719 p->print(this);
2720 if (!had_line && (p->is_single_line() || p->is_double_line())) {
2721 printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r));
2722 had_line = 1;
2725 // change the row *after* printing the stuff list (which might contain .TH)
2726 printfs("\\*[" TRANSPARENT_STRING_NAME "].nr " CURRENT_ROW_REG " %1\n",
2727 as_string(r));
2728 if (!had_line && row_is_all_lines[r])
2729 printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r));
2730 // we might have had a .TH, for example, since we last tried
2731 if (!(flags & NOKEEP) && row_begins_section(r))
2732 prints("." KEEP_MACRO_NAME "\n");
2733 printfs(".mk %1\n", row_start_reg(r));
2734 prints(".mk " BOTTOM_REG "\n"
2735 "." REPEATED_VPT_MACRO " 0\n");
2736 int c;
2737 int row_is_blank = 1;
2738 int first_start_row = r;
2739 for (c = 0; c < ncolumns; c++) {
2740 table_entry *e = entry[r][c];
2741 if (e) {
2742 if (e->end_row == r) {
2743 e->do_depth();
2744 if (e->start_row < first_start_row)
2745 first_start_row = e->start_row;
2746 row_is_blank = 0;
2748 c = e->end_col;
2751 if (row_is_blank)
2752 prints(".nr " BOTTOM_REG " +1v\n");
2753 if (row_is_all_lines[r]) {
2754 prints(".vs " LINE_SEP);
2755 if (row_is_all_lines[r] == 2)
2756 prints("+" DOUBLE_LINE_SEP);
2757 prints(">?\\n[.V]u\n.ls 1\n");
2758 prints("\\&");
2759 prints("\\v'" BODY_DEPTH);
2760 if (row_is_all_lines[r] == 2)
2761 prints("-" HALF_DOUBLE_LINE_SEP);
2762 prints("'");
2763 for (c = 0; c < ncolumns; c++) {
2764 table_entry *e = entry[r][c];
2765 if (e) {
2766 if (e->end_row == e->start_row)
2767 e->to_simple_entry()->simple_print(1);
2768 c = e->end_col;
2771 prints("\n");
2772 prints(".ls\n"
2773 ".vs\n");
2774 prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n");
2775 printfs(".sp |\\n[%1]u\n", row_start_reg(r));
2777 for (int i = row_is_all_lines[r] ? r - 1 : r;
2778 i >= first_start_row;
2779 i--) {
2780 simple_entry *first = 0;
2781 for (c = 0; c < ncolumns; c++) {
2782 table_entry *e = entry[r][c];
2783 if (e) {
2784 if (e->end_row == r && e->start_row == i) {
2785 simple_entry *simple = e->to_simple_entry();
2786 if (simple) {
2787 if (!first) {
2788 prints(".ta");
2789 first = simple;
2791 simple->add_tab();
2794 c = e->end_col;
2797 if (first) {
2798 prints('\n');
2799 first->position_vertically();
2800 first->set_location();
2801 prints("\\&");
2802 first->simple_print(0);
2803 for (c = first->end_col + 1; c < ncolumns; c++) {
2804 table_entry *e = entry[r][c];
2805 if (e) {
2806 if (e->end_row == r && e->start_row == i) {
2807 simple_entry *simple = e->to_simple_entry();
2808 if (simple) {
2809 if (e->end_row != e->start_row) {
2810 prints('\n');
2811 simple->position_vertically();
2812 prints("\\&");
2814 simple->simple_print(0);
2817 c = e->end_col;
2820 prints('\n');
2821 prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n");
2822 printfs(".sp |\\n[%1]u\n", row_start_reg(r));
2825 for (c = 0; c < ncolumns; c++) {
2826 table_entry *e = entry[r][c];
2827 if (e) {
2828 if (e->end_row == r && e->to_simple_entry() == 0) {
2829 e->position_vertically();
2830 e->print();
2831 prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n");
2832 printfs(".sp |\\n[%1]u\n", row_start_reg(r));
2834 c = e->end_col;
2837 prints("." REPEATED_VPT_MACRO " 1\n"
2838 ".sp |\\n[" BOTTOM_REG "]u\n"
2839 "\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG " 1\n");
2840 if (r != nrows - 1 && (flags & ALLBOX)) {
2841 print_single_hline(r + 1);
2842 prints("\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG " 0\n");
2844 if (r != nrows - 1) {
2845 if (p && p->row == r + 1
2846 && (p->is_single_line() || p->is_double_line())) {
2847 p->print(this);
2848 prints("\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG
2849 " 0\n");
2851 int printed_one = 0;
2852 for (vertical_rule *vr = vrule_list; vr; vr = vr->next)
2853 if (vr->end_row == r) {
2854 if (!printed_one) {
2855 prints("." REPEATED_VPT_MACRO " 0\n");
2856 printed_one = 1;
2858 vr->print();
2860 if (printed_one)
2861 prints("." REPEATED_VPT_MACRO " 1\n");
2862 if (!(flags & NOKEEP) && row_ends_section(r))
2863 prints("." RELEASE_MACRO_NAME "\n");
2867 void table::do_top()
2869 prints(".fc \002\003\n");
2870 if (!(flags & NOKEEP) && (flags & (BOX | DOUBLEBOX | ALLBOX)))
2871 prints("." TABLE_KEEP_MACRO_NAME "\n");
2872 if (flags & DOUBLEBOX) {
2873 prints(".ls 1\n"
2874 ".vs " LINE_SEP ">?\\n[.V]u\n"
2875 "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]\\D'l \\n[TW]u 0'\\s0\n"
2876 ".vs\n"
2877 "." REPEATED_MARK_MACRO " " TOP_REG "\n"
2878 ".vs " DOUBLE_LINE_SEP ">?\\n[.V]u\n");
2879 printfs("\\v'" BODY_DEPTH "'"
2880 "\\s[\\n[" LINESIZE_REG "]]"
2881 "\\h'\\n[%1]u'"
2882 "\\D'l |\\n[%2]u 0'"
2883 "\\s0"
2884 "\n",
2885 column_divide_reg(0),
2886 column_divide_reg(ncolumns));
2887 prints(".ls\n"
2888 ".vs\n");
2890 else if (flags & (ALLBOX | BOX)) {
2891 print_single_hline(0);
2893 //printfs(".mk %1\n", row_top_reg(0));
2896 void table::do_bottom()
2898 // print stuff after last row
2899 for (stuff *p = stuff_list; p; p = p->next)
2900 if (p->row > nrows - 1)
2901 p->print(this);
2902 if (!(flags & NOKEEP))
2903 prints("." RELEASE_MACRO_NAME "\n");
2904 printfs(".mk %1\n", row_top_reg(nrows));
2905 prints(".nr " NEED_BOTTOM_RULE_REG " 1\n"
2906 ".nr T. 1\n"
2907 ".T#\n");
2908 if (!(flags & NOKEEP) && (flags & (BOX | DOUBLEBOX | ALLBOX)))
2909 prints("." TABLE_RELEASE_MACRO_NAME "\n");
2910 if (flags & DOUBLEBOX)
2911 prints(".sp " DOUBLE_LINE_SEP "\n");
2912 prints("." RESET_MACRO_NAME "\n"
2913 ".fc\n"
2914 ".cp \\n(" COMPATIBLE_REG "\n");
2917 int table::get_nrows()
2919 return nrows;
2922 const char *last_filename = 0;
2924 void set_troff_location(const char *fn, int ln)
2926 if (!location_force_filename && last_filename != 0
2927 && strcmp(fn, last_filename) == 0)
2928 printfs(".lf %1\n", as_string(ln));
2929 else {
2930 printfs(".lf %1 %2\n", as_string(ln), fn);
2931 last_filename = fn;
2932 location_force_filename = 0;
2936 void printfs(const char *s, const string &arg1, const string &arg2,
2937 const string &arg3, const string &arg4, const string &arg5)
2939 if (s) {
2940 char c;
2941 while ((c = *s++) != '\0') {
2942 if (c == '%') {
2943 switch (*s++) {
2944 case '1':
2945 prints(arg1);
2946 break;
2947 case '2':
2948 prints(arg2);
2949 break;
2950 case '3':
2951 prints(arg3);
2952 break;
2953 case '4':
2954 prints(arg4);
2955 break;
2956 case '5':
2957 prints(arg5);
2958 break;
2959 case '6':
2960 case '7':
2961 case '8':
2962 case '9':
2963 break;
2964 case '%':
2965 prints('%');
2966 break;
2967 default:
2968 assert(0);
2971 else
2972 prints(c);
2977 // s-it2-mode