groff: update vendor branch to v1.20.1
[dragonfly.git] / contrib / groff / src / preproc / tbl / table.cpp
CommitLineData
92d0a6a6 1// -*- C++ -*-
4d3e9548
JL
2/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2003, 2004, 2007, 2008,
3 2009
92d0a6a6
JR
4 Free Software Foundation, Inc.
5 Written by James Clark (jjc@jclark.com)
6
7This file is part of groff.
8
9groff is free software; you can redistribute it and/or modify it under
10the terms of the GNU General Public License as published by the Free
4d3e9548
JL
11Software Foundation, either version 3 of the License, or
12(at your option) any later version.
92d0a6a6
JR
13
14groff is distributed in the hope that it will be useful, but WITHOUT ANY
15WARRANTY; without even the implied warranty of MERCHANTABILITY or
16FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17for more details.
18
4d3e9548
JL
19You should have received a copy of the GNU General Public License
20along with this program. If not, see <http://www.gnu.org/licenses/>. */
92d0a6a6
JR
21
22#include "table.h"
23
24#define BAR_HEIGHT ".25m"
25#define DOUBLE_LINE_SEP "2p"
26#define HALF_DOUBLE_LINE_SEP "1p"
27#define LINE_SEP "2p"
28#define BODY_DEPTH ".25m"
29
30const int DEFAULT_COLUMN_SEPARATION = 3;
31
32#define DELIMITER_CHAR "\\[tbl]"
92d0a6a6
JR
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"
62
63// this must be one character
64#define COMPATIBLE_REG PREFIX "c"
65
4d3e9548
JL
66#define EXPAND_REG PREFIX "expand"
67
465b256c
JR
68#define LEADER_REG PREFIX LEADER
69
92d0a6a6
JR
70#define BLOCK_WIDTH_PREFIX PREFIX "tbw"
71#define BLOCK_DIVERSION_PREFIX PREFIX "tbd"
72#define BLOCK_HEIGHT_PREFIX PREFIX "tbh"
73#define SPAN_WIDTH_PREFIX PREFIX "w"
74#define SPAN_LEFT_NUMERIC_WIDTH_PREFIX PREFIX "lnw"
75#define SPAN_RIGHT_NUMERIC_WIDTH_PREFIX PREFIX "rnw"
76#define SPAN_ALPHABETIC_WIDTH_PREFIX PREFIX "aw"
77#define COLUMN_SEPARATION_PREFIX PREFIX "cs"
78#define ROW_START_PREFIX PREFIX "rs"
79#define COLUMN_START_PREFIX PREFIX "cl"
80#define COLUMN_END_PREFIX PREFIX "ce"
81#define COLUMN_DIVIDE_PREFIX PREFIX "cd"
82#define ROW_TOP_PREFIX PREFIX "rt"
83
4d3e9548
JL
84string block_width_reg(int, int);
85string block_diversion_name(int, int);
86string block_height_reg(int, int);
87string span_width_reg(int, int);
88string span_left_numeric_width_reg(int, int);
89string span_right_numeric_width_reg(int, int);
90string span_alphabetic_width_reg(int, int);
91string column_separation_reg(int);
92string row_start_reg(int);
93string column_start_reg(int);
94string column_end_reg(int);
95string column_divide_reg(int);
96string row_top_reg(int);
92d0a6a6
JR
97
98void set_inline_modifier(const entry_modifier *);
4d3e9548 99void restore_inline_modifier(const entry_modifier *);
92d0a6a6 100void set_modifier(const entry_modifier *);
4d3e9548 101int find_decimal_point(const char *, char, const char *);
92d0a6a6
JR
102
103string an_empty_string;
104int location_force_filename = 0;
105
106void 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);
112
113void prints(const string &);
114
115inline void prints(char c)
116{
117 putchar(c);
118}
119
120inline void prints(const char *s)
121{
122 fputs(s, stdout);
123}
124
125void prints(const string &s)
126{
127 if (!s.empty())
128 fwrite(s.contents(), 1, s.length(), stdout);
129}
130
131struct horizontal_span {
132 horizontal_span *next;
133 int start_col;
134 int end_col;
135 horizontal_span(int, int, horizontal_span *);
136};
137
138class single_line_entry;
139class double_line_entry;
140class simple_entry;
141
142class table_entry {
143friend class table;
144 table_entry *next;
145 int input_lineno;
146 const char *input_filename;
147protected:
148 int start_row;
149 int end_row;
150 int start_col;
151 int end_col;
4d3e9548 152 const table *parent;
92d0a6a6
JR
153 const entry_modifier *mod;
154public:
155 void set_location();
4d3e9548 156 table_entry(const table *, const entry_modifier *);
92d0a6a6 157 virtual ~table_entry();
4d3e9548 158 virtual int divert(int, const string *, int *, int);
92d0a6a6
JR
159 virtual void do_width();
160 virtual void do_depth();
161 virtual void print() = 0;
162 virtual void position_vertically() = 0;
163 virtual single_line_entry *to_single_line_entry();
164 virtual double_line_entry *to_double_line_entry();
165 virtual simple_entry *to_simple_entry();
166 virtual int line_type();
167 virtual void note_double_vrule_on_right(int);
168 virtual void note_double_vrule_on_left(int);
169};
170
171class simple_entry : public table_entry {
172public:
4d3e9548 173 simple_entry(const table *, const entry_modifier *);
92d0a6a6
JR
174 void print();
175 void position_vertically();
176 simple_entry *to_simple_entry();
177 virtual void add_tab();
178 virtual void simple_print(int);
179};
180
181class empty_entry : public simple_entry {
182public:
4d3e9548 183 empty_entry(const table *, const entry_modifier *);
92d0a6a6
JR
184 int line_type();
185};
186
187class text_entry : public simple_entry {
188protected:
189 char *contents;
190 void print_contents();
191public:
4d3e9548 192 text_entry(const table *, const entry_modifier *, char *);
92d0a6a6
JR
193 ~text_entry();
194};
195
196void text_entry::print_contents()
197{
198 set_inline_modifier(mod);
199 prints(contents);
200 restore_inline_modifier(mod);
201}
202
203class repeated_char_entry : public text_entry {
204public:
4d3e9548 205 repeated_char_entry(const table *, const entry_modifier *, char *);
92d0a6a6
JR
206 void simple_print(int);
207};
208
209class simple_text_entry : public text_entry {
210public:
4d3e9548 211 simple_text_entry(const table *, const entry_modifier *, char *);
92d0a6a6
JR
212 void do_width();
213};
214
215class left_text_entry : public simple_text_entry {
216public:
4d3e9548 217 left_text_entry(const table *, const entry_modifier *, char *);
92d0a6a6
JR
218 void simple_print(int);
219 void add_tab();
220};
221
222class right_text_entry : public simple_text_entry {
223public:
4d3e9548 224 right_text_entry(const table *, const entry_modifier *, char *);
92d0a6a6
JR
225 void simple_print(int);
226 void add_tab();
227};
228
229class center_text_entry : public simple_text_entry {
230public:
4d3e9548 231 center_text_entry(const table *, const entry_modifier *, char *);
92d0a6a6
JR
232 void simple_print(int);
233 void add_tab();
234};
235
236class numeric_text_entry : public text_entry {
237 int dot_pos;
238public:
4d3e9548 239 numeric_text_entry(const table *, const entry_modifier *, char *, int);
92d0a6a6
JR
240 void do_width();
241 void simple_print(int);
242};
243
244class alphabetic_text_entry : public text_entry {
245public:
4d3e9548 246 alphabetic_text_entry(const table *, const entry_modifier *, char *);
92d0a6a6
JR
247 void do_width();
248 void simple_print(int);
249 void add_tab();
250};
251
252class line_entry : public simple_entry {
253protected:
254 char double_vrule_on_right;
255 char double_vrule_on_left;
256public:
4d3e9548 257 line_entry(const table *, const entry_modifier *);
92d0a6a6
JR
258 void note_double_vrule_on_right(int);
259 void note_double_vrule_on_left(int);
260 void simple_print(int) = 0;
261};
262
263class single_line_entry : public line_entry {
264public:
4d3e9548 265 single_line_entry(const table *, const entry_modifier *);
92d0a6a6
JR
266 void simple_print(int);
267 single_line_entry *to_single_line_entry();
268 int line_type();
269};
270
271class double_line_entry : public line_entry {
272public:
4d3e9548 273 double_line_entry(const table *, const entry_modifier *);
92d0a6a6
JR
274 void simple_print(int);
275 double_line_entry *to_double_line_entry();
276 int line_type();
277};
278
279class short_line_entry : public simple_entry {
280public:
4d3e9548 281 short_line_entry(const table *, const entry_modifier *);
92d0a6a6
JR
282 void simple_print(int);
283 int line_type();
284};
285
286class short_double_line_entry : public simple_entry {
287public:
4d3e9548 288 short_double_line_entry(const table *, const entry_modifier *);
92d0a6a6
JR
289 void simple_print(int);
290 int line_type();
291};
292
293class block_entry : public table_entry {
294 char *contents;
295protected:
4d3e9548 296 void do_divert(int, int, const string *, int *, int);
92d0a6a6 297public:
4d3e9548 298 block_entry(const table *, const entry_modifier *, char *);
92d0a6a6 299 ~block_entry();
4d3e9548 300 int divert(int, const string *, int *, int);
92d0a6a6
JR
301 void do_depth();
302 void position_vertically();
303 void print() = 0;
304};
305
306class left_block_entry : public block_entry {
307public:
4d3e9548 308 left_block_entry(const table *, const entry_modifier *, char *);
92d0a6a6
JR
309 void print();
310};
311
312class right_block_entry : public block_entry {
313public:
4d3e9548 314 right_block_entry(const table *, const entry_modifier *, char *);
92d0a6a6
JR
315 void print();
316};
317
318class center_block_entry : public block_entry {
319public:
4d3e9548 320 center_block_entry(const table *, const entry_modifier *, char *);
92d0a6a6
JR
321 void print();
322};
323
324class alphabetic_block_entry : public block_entry {
325public:
4d3e9548 326 alphabetic_block_entry(const table *, const entry_modifier *, char *);
92d0a6a6 327 void print();
4d3e9548 328 int divert(int, const string *, int *, int);
92d0a6a6
JR
329};
330
4d3e9548 331table_entry::table_entry(const table *p, const entry_modifier *m)
92d0a6a6 332: next(0), input_lineno(-1), input_filename(0),
4d3e9548 333 start_row(-1), end_row(-1), start_col(-1), end_col(-1), parent(p), mod(m)
92d0a6a6
JR
334{
335}
336
337table_entry::~table_entry()
338{
339}
340
4d3e9548 341int table_entry::divert(int, const string *, int *, int)
92d0a6a6
JR
342{
343 return 0;
344}
345
346void table_entry::do_width()
347{
348}
349
350single_line_entry *table_entry::to_single_line_entry()
351{
352 return 0;
353}
354
355double_line_entry *table_entry::to_double_line_entry()
356{
357 return 0;
358}
359
360simple_entry *table_entry::to_simple_entry()
361{
362 return 0;
363}
364
365void table_entry::do_depth()
366{
367}
368
369void table_entry::set_location()
370{
371 set_troff_location(input_filename, input_lineno);
372}
373
374int table_entry::line_type()
375{
376 return -1;
377}
378
379void table_entry::note_double_vrule_on_right(int)
380{
381}
382
383void table_entry::note_double_vrule_on_left(int)
384{
385}
386
4d3e9548
JL
387simple_entry::simple_entry(const table *p, const entry_modifier *m)
388: table_entry(p, m)
92d0a6a6
JR
389{
390}
391
392void simple_entry::add_tab()
393{
394 // do nothing
395}
396
397void simple_entry::simple_print(int)
398{
399 // do nothing
400}
401
402void simple_entry::position_vertically()
403{
404 if (start_row != end_row)
405 switch (mod->vertical_alignment) {
406 case entry_modifier::TOP:
407 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
408 break;
409 case entry_modifier::CENTER:
410 // Peform the motion in two stages so that the center is rounded
411 // vertically upwards even if net vertical motion is upwards.
412 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
413 printfs(".sp \\n[" BOTTOM_REG "]u-\\n[%1]u-1v/2u\n",
414 row_start_reg(start_row));
415 break;
416 case entry_modifier::BOTTOM:
417 printfs(".sp |\\n[%1]u+\\n[" BOTTOM_REG "]u-\\n[%1]u-1v\n",
418 row_start_reg(start_row));
419 break;
420 default:
421 assert(0);
422 }
423}
424
425void simple_entry::print()
426{
427 prints(".ta");
428 add_tab();
429 prints('\n');
430 set_location();
431 prints("\\&");
432 simple_print(0);
433 prints('\n');
434}
435
436simple_entry *simple_entry::to_simple_entry()
437{
438 return this;
439}
440
4d3e9548
JL
441empty_entry::empty_entry(const table *p, const entry_modifier *m)
442: simple_entry(p, m)
92d0a6a6
JR
443{
444}
445
446int empty_entry::line_type()
447{
448 return 0;
449}
450
4d3e9548
JL
451text_entry::text_entry(const table *p, const entry_modifier *m, char *s)
452: simple_entry(p, m), contents(s)
92d0a6a6
JR
453{
454}
455
456text_entry::~text_entry()
457{
458 a_delete contents;
459}
460
4d3e9548
JL
461repeated_char_entry::repeated_char_entry(const table *p,
462 const entry_modifier *m, char *s)
463: text_entry(p, m, s)
92d0a6a6
JR
464{
465}
466
467void repeated_char_entry::simple_print(int)
468{
469 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
470 set_inline_modifier(mod);
471 printfs("\\l" DELIMITER_CHAR "\\n[%1]u\\&",
472 span_width_reg(start_col, end_col));
473 prints(contents);
474 prints(DELIMITER_CHAR);
475 restore_inline_modifier(mod);
476}
477
4d3e9548
JL
478simple_text_entry::simple_text_entry(const table *p,
479 const entry_modifier *m, char *s)
480: text_entry(p, m, s)
92d0a6a6
JR
481{
482}
483
484void simple_text_entry::do_width()
485{
486 set_location();
487 printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR,
488 span_width_reg(start_col, end_col));
489 print_contents();
490 prints(DELIMITER_CHAR "\n");
491}
492
4d3e9548
JL
493left_text_entry::left_text_entry(const table *p,
494 const entry_modifier *m, char *s)
495: simple_text_entry(p, m, s)
92d0a6a6
JR
496{
497}
498
499void left_text_entry::simple_print(int)
500{
501 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
502 print_contents();
503}
504
505// The only point of this is to make `\a' ``work'' as in Unix tbl. Grrr.
506
507void left_text_entry::add_tab()
508{
509 printfs(" \\n[%1]u", column_end_reg(end_col));
510}
511
4d3e9548
JL
512right_text_entry::right_text_entry(const table *p,
513 const entry_modifier *m, char *s)
514: simple_text_entry(p, m, s)
92d0a6a6
JR
515{
516}
517
518void right_text_entry::simple_print(int)
519{
520 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
521 prints("\002\003");
522 print_contents();
523 prints("\002");
524}
525
526void right_text_entry::add_tab()
527{
528 printfs(" \\n[%1]u", column_end_reg(end_col));
529}
530
4d3e9548
JL
531center_text_entry::center_text_entry(const table *p,
532 const entry_modifier *m, char *s)
533: simple_text_entry(p, m, s)
92d0a6a6
JR
534{
535}
536
537void center_text_entry::simple_print(int)
538{
539 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
540 prints("\002\003");
541 print_contents();
542 prints("\003\002");
543}
544
545void center_text_entry::add_tab()
546{
547 printfs(" \\n[%1]u", column_end_reg(end_col));
548}
549
4d3e9548
JL
550numeric_text_entry::numeric_text_entry(const table *p,
551 const entry_modifier *m,
552 char *s, int pos)
553: text_entry(p, m, s), dot_pos(pos)
92d0a6a6
JR
554{
555}
556
557void numeric_text_entry::do_width()
558{
559 if (dot_pos != 0) {
560 set_location();
561 printfs(".nr %1 0\\w" DELIMITER_CHAR,
562 block_width_reg(start_row, start_col));
563 set_inline_modifier(mod);
564 for (int i = 0; i < dot_pos; i++)
565 prints(contents[i]);
566 restore_inline_modifier(mod);
567 prints(DELIMITER_CHAR "\n");
568 printfs(".nr %1 \\n[%1]>?\\n[%2]\n",
569 span_left_numeric_width_reg(start_col, end_col),
570 block_width_reg(start_row, start_col));
571 }
572 else
573 printfs(".nr %1 0\n", block_width_reg(start_row, start_col));
574 if (contents[dot_pos] != '\0') {
575 set_location();
576 printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR,
577 span_right_numeric_width_reg(start_col, end_col));
578 set_inline_modifier(mod);
579 prints(contents + dot_pos);
580 restore_inline_modifier(mod);
581 prints(DELIMITER_CHAR "\n");
582 }
583}
584
585void numeric_text_entry::simple_print(int)
586{
587 printfs("\\h'|(\\n[%1]u-\\n[%2]u-\\n[%3]u/2u+\\n[%2]u+\\n[%4]u-\\n[%5]u)'",
588 span_width_reg(start_col, end_col),
589 span_left_numeric_width_reg(start_col, end_col),
590 span_right_numeric_width_reg(start_col, end_col),
591 column_start_reg(start_col),
592 block_width_reg(start_row, start_col));
593 print_contents();
594}
595
4d3e9548
JL
596alphabetic_text_entry::alphabetic_text_entry(const table *p,
597 const entry_modifier *m,
598 char *s)
599: text_entry(p, m, s)
92d0a6a6
JR
600{
601}
602
603void alphabetic_text_entry::do_width()
604{
605 set_location();
606 printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR,
607 span_alphabetic_width_reg(start_col, end_col));
608 print_contents();
609 prints(DELIMITER_CHAR "\n");
610}
611
612void alphabetic_text_entry::simple_print(int)
613{
614 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
615 printfs("\\h'\\n[%1]u-\\n[%2]u/2u'",
616 span_width_reg(start_col, end_col),
617 span_alphabetic_width_reg(start_col, end_col));
618 print_contents();
619}
620
621// The only point of this is to make `\a' ``work'' as in Unix tbl. Grrr.
622
623void alphabetic_text_entry::add_tab()
624{
625 printfs(" \\n[%1]u", column_end_reg(end_col));
626}
627
4d3e9548
JL
628block_entry::block_entry(const table *p, const entry_modifier *m, char *s)
629: table_entry(p, m), contents(s)
92d0a6a6
JR
630{
631}
632
633block_entry::~block_entry()
634{
635 a_delete contents;
636}
637
638void block_entry::position_vertically()
639{
640 if (start_row != end_row)
641 switch(mod->vertical_alignment) {
642 case entry_modifier::TOP:
643 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
644 break;
645 case entry_modifier::CENTER:
646 // Peform the motion in two stages so that the center is rounded
647 // vertically upwards even if net vertical motion is upwards.
648 printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
649 printfs(".sp \\n[" BOTTOM_REG "]u-\\n[%1]u-\\n[%2]u/2u\n",
650 row_start_reg(start_row),
651 block_height_reg(start_row, start_col));
652 break;
653 case entry_modifier::BOTTOM:
654 printfs(".sp |\\n[%1]u+\\n[" BOTTOM_REG "]u-\\n[%1]u-\\n[%2]u\n",
655 row_start_reg(start_row),
656 block_height_reg(start_row, start_col));
657 break;
658 default:
659 assert(0);
660 }
661 if (mod->stagger)
662 prints(".sp -.5v\n");
663}
664
4d3e9548 665int block_entry::divert(int ncols, const string *mw, int *sep, int do_expand)
92d0a6a6 666{
4d3e9548 667 do_divert(0, ncols, mw, sep, do_expand);
92d0a6a6
JR
668 return 1;
669}
670
671void block_entry::do_divert(int alphabetic, int ncols, const string *mw,
4d3e9548 672 int *sep, int do_expand)
92d0a6a6 673{
4d3e9548
JL
674 int i;
675 for (i = start_col; i <= end_col; i++)
676 if (parent->expand[i])
677 break;
678 if (i > end_col) {
679 if (do_expand)
680 return;
681 }
682 else {
683 if (!do_expand)
684 return;
685 }
92d0a6a6
JR
686 printfs(".di %1\n", block_diversion_name(start_row, start_col));
687 prints(".if \\n[" SAVED_FILL_REG "] .fi\n"
688 ".in 0\n");
689 prints(".ll ");
92d0a6a6 690 for (i = start_col; i <= end_col; i++)
4d3e9548 691 if (mw[i].empty() && !parent->expand[i])
92d0a6a6
JR
692 break;
693 if (i > end_col) {
694 // Every column spanned by this entry has a minimum width.
695 for (int j = start_col; j <= end_col; j++) {
696 if (j > start_col) {
697 if (sep)
698 printfs("+%1n", as_string(sep[j - 1]));
699 prints('+');
700 }
4d3e9548
JL
701 if (parent->expand[j])
702 prints("\\n[" EXPAND_REG "]u");
703 else
704 printfs("(n;%1)", mw[j]);
92d0a6a6
JR
705 }
706 printfs(">?\\n[%1]u", span_width_reg(start_col, end_col));
707 }
708 else
4d3e9548
JL
709 // Assign each column with a block entry 1/(n+1) of the line
710 // width, where n is the column count.
92d0a6a6
JR
711 printfs("(u;\\n[%1]>?(\\n[.l]*%2/%3))",
712 span_width_reg(start_col, end_col),
713 as_string(end_col - start_col + 1),
714 as_string(ncols + 1));
715 if (alphabetic)
716 prints("-2n");
717 prints("\n");
718 prints(".cp \\n(" COMPATIBLE_REG "\n");
719 set_modifier(mod);
720 set_location();
721 prints(contents);
722 prints(".br\n.di\n.cp 0\n");
723 if (!mod->zero_width) {
724 if (alphabetic) {
725 printfs(".nr %1 \\n[%1]>?(\\n[dl]+2n)\n",
726 span_width_reg(start_col, end_col));
727 printfs(".nr %1 \\n[%1]>?\\n[dl]\n",
728 span_alphabetic_width_reg(start_col, end_col));
729 }
730 else
4d3e9548
JL
731 printfs(".nr %1 \\n[%1]>?\\n[dl]\n",
732 span_width_reg(start_col, end_col));
92d0a6a6
JR
733 }
734 printfs(".nr %1 \\n[dn]\n", block_height_reg(start_row, start_col));
735 printfs(".nr %1 \\n[dl]\n", block_width_reg(start_row, start_col));
736 prints("." RESET_MACRO_NAME "\n"
737 ".in \\n[" SAVED_INDENT_REG "]u\n"
738 ".nf\n");
739 // the block might have contained .lf commands
740 location_force_filename = 1;
741}
742
92d0a6a6
JR
743void block_entry::do_depth()
744{
745 printfs(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?(\\n[%1]+\\n[%2])\n",
746 row_start_reg(start_row),
747 block_height_reg(start_row, start_col));
748}
749
4d3e9548
JL
750left_block_entry::left_block_entry(const table *p,
751 const entry_modifier *m, char *s)
752: block_entry(p, m, s)
92d0a6a6
JR
753{
754}
755
756void left_block_entry::print()
757{
758 printfs(".in +\\n[%1]u\n", column_start_reg(start_col));
759 printfs(".%1\n", block_diversion_name(start_row, start_col));
760 prints(".in\n");
761}
762
4d3e9548
JL
763right_block_entry::right_block_entry(const table *p,
764 const entry_modifier *m, char *s)
765: block_entry(p, m, s)
92d0a6a6
JR
766{
767}
768
769void right_block_entry::print()
770{
771 printfs(".in +\\n[%1]u+\\n[%2]u-\\n[%3]u\n",
772 column_start_reg(start_col),
773 span_width_reg(start_col, end_col),
774 block_width_reg(start_row, start_col));
775 printfs(".%1\n", block_diversion_name(start_row, start_col));
776 prints(".in\n");
777}
778
4d3e9548
JL
779center_block_entry::center_block_entry(const table *p,
780 const entry_modifier *m, char *s)
781: block_entry(p, m, s)
92d0a6a6
JR
782{
783}
784
785void center_block_entry::print()
786{
787 printfs(".in +\\n[%1]u+(\\n[%2]u-\\n[%3]u/2u)\n",
788 column_start_reg(start_col),
789 span_width_reg(start_col, end_col),
790 block_width_reg(start_row, start_col));
791 printfs(".%1\n", block_diversion_name(start_row, start_col));
792 prints(".in\n");
793}
794
4d3e9548
JL
795alphabetic_block_entry::alphabetic_block_entry(const table *p,
796 const entry_modifier *m,
797 char *s)
798: block_entry(p, m, s)
92d0a6a6
JR
799{
800}
801
4d3e9548
JL
802int alphabetic_block_entry::divert(int ncols, const string *mw, int *sep,
803 int do_expand)
92d0a6a6 804{
4d3e9548 805 do_divert(1, ncols, mw, sep, do_expand);
92d0a6a6
JR
806 return 1;
807}
808
809void alphabetic_block_entry::print()
810{
811 printfs(".in +\\n[%1]u+(\\n[%2]u-\\n[%3]u/2u)\n",
812 column_start_reg(start_col),
813 span_width_reg(start_col, end_col),
814 span_alphabetic_width_reg(start_col, end_col));
815 printfs(".%1\n", block_diversion_name(start_row, start_col));
816 prints(".in\n");
817}
818
4d3e9548
JL
819line_entry::line_entry(const table *p, const entry_modifier *m)
820: simple_entry(p, m), double_vrule_on_right(0), double_vrule_on_left(0)
92d0a6a6
JR
821{
822}
823
824void line_entry::note_double_vrule_on_right(int is_corner)
825{
826 double_vrule_on_right = is_corner ? 1 : 2;
827}
828
829void line_entry::note_double_vrule_on_left(int is_corner)
830{
831 double_vrule_on_left = is_corner ? 1 : 2;
832}
833
4d3e9548
JL
834single_line_entry::single_line_entry(const table *p, const entry_modifier *m)
835: line_entry(p, m)
92d0a6a6
JR
836{
837}
838
839int single_line_entry::line_type()
840{
841 return 1;
842}
843
844void single_line_entry::simple_print(int dont_move)
845{
846 printfs("\\h'|\\n[%1]u",
847 column_divide_reg(start_col));
848 if (double_vrule_on_left) {
849 prints(double_vrule_on_left == 1 ? "-" : "+");
850 prints(HALF_DOUBLE_LINE_SEP);
851 }
852 prints("'");
853 if (!dont_move)
854 prints("\\v'-" BAR_HEIGHT "'");
855 printfs("\\s[\\n[" LINESIZE_REG "]]" "\\D'l |\\n[%1]u",
856 column_divide_reg(end_col+1));
857 if (double_vrule_on_right) {
858 prints(double_vrule_on_left == 1 ? "+" : "-");
859 prints(HALF_DOUBLE_LINE_SEP);
860 }
861 prints("0'\\s0");
862 if (!dont_move)
863 prints("\\v'" BAR_HEIGHT "'");
864}
865
866single_line_entry *single_line_entry::to_single_line_entry()
867{
868 return this;
869}
870
4d3e9548
JL
871double_line_entry::double_line_entry(const table *p, const entry_modifier *m)
872: line_entry(p, m)
92d0a6a6
JR
873{
874}
875
876int double_line_entry::line_type()
877{
878 return 2;
879}
880
881void double_line_entry::simple_print(int dont_move)
882{
883 if (!dont_move)
884 prints("\\v'-" BAR_HEIGHT "'");
885 printfs("\\h'|\\n[%1]u",
886 column_divide_reg(start_col));
887 if (double_vrule_on_left) {
888 prints(double_vrule_on_left == 1 ? "-" : "+");
889 prints(HALF_DOUBLE_LINE_SEP);
890 }
891 prints("'");
892 printfs("\\v'-" HALF_DOUBLE_LINE_SEP "'"
893 "\\s[\\n[" LINESIZE_REG "]]"
894 "\\D'l |\\n[%1]u",
895 column_divide_reg(end_col+1));
896 if (double_vrule_on_right)
897 prints("-" HALF_DOUBLE_LINE_SEP);
898 prints(" 0'");
899 printfs("\\v'" DOUBLE_LINE_SEP "'"
900 "\\D'l |\\n[%1]u",
901 column_divide_reg(start_col));
902 if (double_vrule_on_right) {
903 prints(double_vrule_on_left == 1 ? "+" : "-");
904 prints(HALF_DOUBLE_LINE_SEP);
905 }
906 prints(" 0'");
907 prints("\\s0"
908 "\\v'-" HALF_DOUBLE_LINE_SEP "'");
909 if (!dont_move)
910 prints("\\v'" BAR_HEIGHT "'");
911}
912
913double_line_entry *double_line_entry::to_double_line_entry()
914{
915 return this;
916}
917
4d3e9548
JL
918short_line_entry::short_line_entry(const table *p, const entry_modifier *m)
919: simple_entry(p, m)
92d0a6a6
JR
920{
921}
922
923int short_line_entry::line_type()
924{
925 return 1;
926}
927
928void short_line_entry::simple_print(int dont_move)
929{
930 if (mod->stagger)
931 prints("\\v'-.5v'");
932 if (!dont_move)
933 prints("\\v'-" BAR_HEIGHT "'");
934 printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
935 printfs("\\s[\\n[" LINESIZE_REG "]]"
936 "\\D'l \\n[%1]u 0'"
937 "\\s0",
938 span_width_reg(start_col, end_col));
939 if (!dont_move)
940 prints("\\v'" BAR_HEIGHT "'");
941 if (mod->stagger)
942 prints("\\v'.5v'");
943}
944
4d3e9548
JL
945short_double_line_entry::short_double_line_entry(const table *p,
946 const entry_modifier *m)
947: simple_entry(p, m)
92d0a6a6
JR
948{
949}
950
951int short_double_line_entry::line_type()
952{
953 return 2;
954}
955
956void short_double_line_entry::simple_print(int dont_move)
957{
958 if (mod->stagger)
959 prints("\\v'-.5v'");
960 if (!dont_move)
961 prints("\\v'-" BAR_HEIGHT "'");
962 printfs("\\h'|\\n[%2]u'"
963 "\\v'-" HALF_DOUBLE_LINE_SEP "'"
964 "\\s[\\n[" LINESIZE_REG "]]"
965 "\\D'l \\n[%1]u 0'"
966 "\\v'" DOUBLE_LINE_SEP "'"
967 "\\D'l |\\n[%2]u 0'"
968 "\\s0"
969 "\\v'-" HALF_DOUBLE_LINE_SEP "'",
970 span_width_reg(start_col, end_col),
971 column_start_reg(start_col));
972 if (!dont_move)
973 prints("\\v'" BAR_HEIGHT "'");
974 if (mod->stagger)
975 prints("\\v'.5v'");
976}
977
978void set_modifier(const entry_modifier *m)
979{
980 if (!m->font.empty())
981 printfs(".ft %1\n", m->font);
982 if (m->point_size.val != 0) {
983 prints(".ps ");
984 if (m->point_size.inc > 0)
985 prints('+');
986 else if (m->point_size.inc < 0)
987 prints('-');
988 printfs("%1\n", as_string(m->point_size.val));
989 }
990 if (m->vertical_spacing.val != 0) {
991 prints(".vs ");
992 if (m->vertical_spacing.inc > 0)
993 prints('+');
994 else if (m->vertical_spacing.inc < 0)
995 prints('-');
996 printfs("%1\n", as_string(m->vertical_spacing.val));
997 }
998 if (!m->macro.empty())
999 printfs(".%1\n", m->macro);
1000}
1001
1002void set_inline_modifier(const entry_modifier *m)
1003{
1004 if (!m->font.empty())
1005 printfs("\\f[%1]", m->font);
1006 if (m->point_size.val != 0) {
1007 prints("\\s[");
1008 if (m->point_size.inc > 0)
1009 prints('+');
1010 else if (m->point_size.inc < 0)
1011 prints('-');
1012 printfs("%1]", as_string(m->point_size.val));
1013 }
1014 if (m->stagger)
1015 prints("\\v'-.5v'");
1016}
1017
1018void restore_inline_modifier(const entry_modifier *m)
1019{
1020 if (!m->font.empty())
1021 prints("\\f[\\n[" SAVED_FONT_REG "]]");
1022 if (m->point_size.val != 0)
1023 prints("\\s[\\n[" SAVED_SIZE_REG "]]");
1024 if (m->stagger)
1025 prints("\\v'.5v'");
1026}
1027
1028struct stuff {
1029 stuff *next;
1030 int row; // occurs before row `row'
1031 char printed; // has it been printed?
1032
1033 stuff(int);
1034 virtual void print(table *) = 0;
1035 virtual ~stuff();
1036 virtual int is_single_line() { return 0; };
1037 virtual int is_double_line() { return 0; };
1038};
1039
1040stuff::stuff(int r) : next(0), row(r), printed(0)
1041{
1042}
1043
1044stuff::~stuff()
1045{
1046}
1047
1048struct text_stuff : public stuff {
1049 string contents;
1050 const char *filename;
1051 int lineno;
1052
4d3e9548 1053 text_stuff(const string &, int, const char *, int);
92d0a6a6
JR
1054 ~text_stuff();
1055 void print(table *);
1056};
1057
1058text_stuff::text_stuff(const string &s, int r, const char *fn, int ln)
1059: stuff(r), contents(s), filename(fn), lineno(ln)
1060{
1061}
1062
1063text_stuff::~text_stuff()
1064{
1065}
1066
1067void text_stuff::print(table *)
1068{
1069 printed = 1;
1070 prints(".cp \\n(" COMPATIBLE_REG "\n");
1071 set_troff_location(filename, lineno);
1072 prints(contents);
1073 prints(".cp 0\n");
1074 location_force_filename = 1; // it might have been a .lf command
1075}
1076
1077struct single_hline_stuff : public stuff {
4d3e9548 1078 single_hline_stuff(int);
92d0a6a6
JR
1079 void print(table *);
1080 int is_single_line();
1081};
1082
1083single_hline_stuff::single_hline_stuff(int r) : stuff(r)
1084{
1085}
1086
1087void single_hline_stuff::print(table *tbl)
1088{
1089 printed = 1;
1090 tbl->print_single_hline(row);
1091}
1092
1093int single_hline_stuff::is_single_line()
1094{
1095 return 1;
1096}
1097
1098struct double_hline_stuff : stuff {
4d3e9548 1099 double_hline_stuff(int);
92d0a6a6
JR
1100 void print(table *);
1101 int is_double_line();
1102};
1103
1104double_hline_stuff::double_hline_stuff(int r) : stuff(r)
1105{
1106}
1107
1108void double_hline_stuff::print(table *tbl)
1109{
1110 printed = 1;
1111 tbl->print_double_hline(row);
1112}
1113
1114int double_hline_stuff::is_double_line()
1115{
1116 return 1;
1117}
1118
1119struct vertical_rule {
1120 vertical_rule *next;
1121 int start_row;
1122 int end_row;
1123 int col;
1124 char is_double;
1125 string top_adjust;
1126 string bot_adjust;
1127
4d3e9548 1128 vertical_rule(int, int, int, int, vertical_rule *);
92d0a6a6
JR
1129 ~vertical_rule();
1130 void contribute_to_bottom_macro(table *);
1131 void print();
1132};
1133
4d3e9548
JL
1134vertical_rule::vertical_rule(int sr, int er, int c, int dbl,
1135 vertical_rule *p)
92d0a6a6
JR
1136: next(p), start_row(sr), end_row(er), col(c), is_double(dbl)
1137{
1138}
1139
1140vertical_rule::~vertical_rule()
1141{
1142}
1143
1144void vertical_rule::contribute_to_bottom_macro(table *tbl)
1145{
1146 printfs(".if \\n[" CURRENT_ROW_REG "]>=%1",
1147 as_string(start_row));
1148 if (end_row != tbl->get_nrows() - 1)
1149 printfs("&(\\n[" CURRENT_ROW_REG "]<%1)",
1150 as_string(end_row));
1151 prints(" \\{");
1152 printfs(".if %1<=\\n[" LAST_PASSED_ROW_REG "] .nr %2 \\n[#T]\n",
1153 as_string(start_row),
1154 row_top_reg(start_row));
1155 const char *offset_table[3];
1156 if (is_double) {
1157 offset_table[0] = "-" HALF_DOUBLE_LINE_SEP;
1158 offset_table[1] = "+" HALF_DOUBLE_LINE_SEP;
1159 offset_table[2] = 0;
1160 }
1161 else {
1162 offset_table[0] = "";
1163 offset_table[1] = 0;
1164 }
1165 for (const char **offsetp = offset_table; *offsetp; offsetp++) {
1166 prints(".sp -1\n"
1167 "\\v'" BODY_DEPTH);
1168 if (!bot_adjust.empty())
1169 printfs("+%1", bot_adjust);
1170 prints("'");
1171 printfs("\\h'\\n[%1]u%3'\\s[\\n[" LINESIZE_REG "]]\\D'l 0 |\\n[%2]u-1v",
1172 column_divide_reg(col),
1173 row_top_reg(start_row),
1174 *offsetp);
1175 if (!bot_adjust.empty())
1176 printfs("-(%1)", bot_adjust);
1177 // don't perform the top adjustment if the top is actually #T
1178 if (!top_adjust.empty())
1179 printfs("+((%1)*(%2>\\n[" LAST_PASSED_ROW_REG "]))",
1180 top_adjust,
1181 as_string(start_row));
1182 prints("'\\s0\n");
1183 }
1184 prints(".\\}\n");
1185}
1186
1187void vertical_rule::print()
1188{
1189 printfs("\\*[" TRANSPARENT_STRING_NAME "]"
1190 ".if %1<=\\*[" QUOTE_STRING_NAME "]\\n[" LAST_PASSED_ROW_REG "] "
1191 ".nr %2 \\*[" QUOTE_STRING_NAME "]\\n[#T]\n",
1192 as_string(start_row),
1193 row_top_reg(start_row));
1194 const char *offset_table[3];
1195 if (is_double) {
1196 offset_table[0] = "-" HALF_DOUBLE_LINE_SEP;
1197 offset_table[1] = "+" HALF_DOUBLE_LINE_SEP;
1198 offset_table[2] = 0;
1199 }
1200 else {
1201 offset_table[0] = "";
1202 offset_table[1] = 0;
1203 }
1204 for (const char **offsetp = offset_table; *offsetp; offsetp++) {
1205 prints("\\*[" TRANSPARENT_STRING_NAME "].sp -1\n"
1206 "\\*[" TRANSPARENT_STRING_NAME "]\\v'" BODY_DEPTH);
1207 if (!bot_adjust.empty())
1208 printfs("+%1", bot_adjust);
1209 prints("'");
1210 printfs("\\h'\\n[%1]u%3'"
1211 "\\s[\\n[" LINESIZE_REG "]]"
1212 "\\D'l 0 |\\*[" QUOTE_STRING_NAME "]\\n[%2]u-1v",
1213 column_divide_reg(col),
1214 row_top_reg(start_row),
1215 *offsetp);
1216 if (!bot_adjust.empty())
1217 printfs("-(%1)", bot_adjust);
1218 // don't perform the top adjustment if the top is actually #T
1219 if (!top_adjust.empty())
1220 printfs("+((%1)*(%2>\\*[" QUOTE_STRING_NAME "]\\n["
1221 LAST_PASSED_ROW_REG "]))",
1222 top_adjust,
1223 as_string(start_row));
1224 prints("'"
1225 "\\s0\n");
1226 }
1227}
1228
1229table::table(int nc, unsigned f, int ls, char dpc)
4d3e9548 1230: nrows(0), ncolumns(nc), linesize(ls), decimal_point_char(dpc),
92d0a6a6
JR
1231 vrule_list(0), stuff_list(0), span_list(0),
1232 entry_list(0), entry_list_tailp(&entry_list), entry(0),
1233 vline(0), row_is_all_lines(0), left_separation(0), right_separation(0),
4d3e9548 1234 total_separation(0), allocated_rows(0), flags(f)
92d0a6a6
JR
1235{
1236 minimum_width = new string[ncolumns];
1237 column_separation = ncolumns > 1 ? new int[ncolumns - 1] : 0;
1238 equal = new char[ncolumns];
4d3e9548 1239 expand = new char[ncolumns];
92d0a6a6 1240 int i;
4d3e9548 1241 for (i = 0; i < ncolumns; i++) {
92d0a6a6 1242 equal[i] = 0;
4d3e9548
JL
1243 expand[i] = 0;
1244 }
1245 for (i = 0; i < ncolumns - 1; i++)
92d0a6a6
JR
1246 column_separation[i] = DEFAULT_COLUMN_SEPARATION;
1247 delim[0] = delim[1] = '\0';
1248}
1249
1250table::~table()
1251{
1252 for (int i = 0; i < nrows; i++) {
1253 a_delete entry[i];
1254 a_delete vline[i];
1255 }
1256 a_delete entry;
1257 a_delete vline;
1258 while (entry_list) {
1259 table_entry *tem = entry_list;
1260 entry_list = entry_list->next;
1261 delete tem;
1262 }
1263 ad_delete(ncolumns) minimum_width;
1264 a_delete column_separation;
1265 a_delete equal;
4d3e9548 1266 a_delete expand;
92d0a6a6
JR
1267 while (stuff_list) {
1268 stuff *tem = stuff_list;
1269 stuff_list = stuff_list->next;
1270 delete tem;
1271 }
1272 while (vrule_list) {
1273 vertical_rule *tem = vrule_list;
1274 vrule_list = vrule_list->next;
1275 delete tem;
1276 }
1277 a_delete row_is_all_lines;
1278 while (span_list) {
1279 horizontal_span *tem = span_list;
1280 span_list = span_list->next;
1281 delete tem;
1282 }
1283}
1284
1285void table::set_delim(char c1, char c2)
1286{
1287 delim[0] = c1;
1288 delim[1] = c2;
1289}
1290
1291void table::set_minimum_width(int c, const string &w)
1292{
1293 assert(c >= 0 && c < ncolumns);
1294 minimum_width[c] = w;
1295}
1296
1297void table::set_column_separation(int c, int n)
1298{
1299 assert(c >= 0 && c < ncolumns - 1);
1300 column_separation[c] = n;
1301}
1302
1303void table::set_equal_column(int c)
1304{
1305 assert(c >= 0 && c < ncolumns);
1306 equal[c] = 1;
1307}
1308
4d3e9548
JL
1309void table::set_expand_column(int c)
1310{
1311 assert(c >= 0 && c < ncolumns);
1312 expand[c] = 1;
1313}
1314
92d0a6a6
JR
1315void table::add_stuff(stuff *p)
1316{
1317 stuff **pp;
1318 for (pp = &stuff_list; *pp; pp = &(*pp)->next)
1319 ;
1320 *pp = p;
1321}
1322
4d3e9548
JL
1323void table::add_text_line(int r, const string &s, const char *filename,
1324 int lineno)
92d0a6a6
JR
1325{
1326 add_stuff(new text_stuff(s, r, filename, lineno));
1327}
1328
1329void table::add_single_hline(int r)
1330{
1331 add_stuff(new single_hline_stuff(r));
1332}
1333
1334void table::add_double_hline(int r)
1335{
1336 add_stuff(new double_hline_stuff(r));
1337}
1338
1339void table::allocate(int r)
1340{
1341 if (r >= nrows) {
1342 typedef table_entry **PPtable_entry; // work around g++ 1.36.1 bug
1343 if (r >= allocated_rows) {
1344 if (allocated_rows == 0) {
1345 allocated_rows = 16;
1346 if (allocated_rows <= r)
1347 allocated_rows = r + 1;
1348 entry = new PPtable_entry[allocated_rows];
1349 vline = new char*[allocated_rows];
1350 }
1351 else {
1352 table_entry ***old_entry = entry;
1353 int old_allocated_rows = allocated_rows;
1354 allocated_rows *= 2;
1355 if (allocated_rows <= r)
1356 allocated_rows = r + 1;
1357 entry = new PPtable_entry[allocated_rows];
1358 memcpy(entry, old_entry, sizeof(table_entry**)*old_allocated_rows);
1359 a_delete old_entry;
1360 char **old_vline = vline;
1361 vline = new char*[allocated_rows];
1362 memcpy(vline, old_vline, sizeof(char*)*old_allocated_rows);
1363 a_delete old_vline;
1364 }
1365 }
1366 assert(allocated_rows > r);
1367 while (nrows <= r) {
1368 entry[nrows] = new table_entry*[ncolumns];
1369 int i;
1370 for (i = 0; i < ncolumns; i++)
1371 entry[nrows][i] = 0;
1372 vline[nrows] = new char[ncolumns+1];
1373 for (i = 0; i < ncolumns+1; i++)
1374 vline[nrows][i] = 0;
1375 nrows++;
1376 }
1377 }
1378}
1379
1380void table::do_hspan(int r, int c)
1381{
1382 assert(r >= 0 && c >= 0 && r < nrows && c < ncolumns);
1383 if (c == 0) {
1384 error("first column cannot be horizontally spanned");
1385 return;
1386 }
1387 table_entry *e = entry[r][c];
1388 if (e) {
1389 assert(e->start_row <= r && r <= e->end_row
1390 && e->start_col <= c && c <= e->end_col
1391 && e->end_row - e->start_row > 0
1392 && e->end_col - e->start_col > 0);
1393 return;
1394 }
1395 e = entry[r][c-1];
1396 // e can be 0 if we had an empty entry or an error
1397 if (e == 0)
1398 return;
1399 if (e->start_row != r) {
1400 /*
1401 l l
1402 ^ s */
1403 error("impossible horizontal span at row %1, column %2", r + 1, c + 1);
1404 }
1405 else {
1406 e->end_col = c;
1407 entry[r][c] = e;
1408 }
1409}
1410
1411void table::do_vspan(int r, int c)
1412{
1413 assert(r >= 0 && c >= 0 && r < nrows && c < ncolumns);
1414 if (r == 0) {
1415 error("first row cannot be vertically spanned");
1416 return;
1417 }
1418 table_entry *e = entry[r][c];
1419 if (e) {
1420 assert(e->start_row <= r && r <= e->end_row
1421 && e->start_col <= c && c <= e->end_col
1422 && e->end_row - e->start_row > 0
1423 && e->end_col - e->start_col > 0);
1424 return;
1425 }
1426 e = entry[r-1][c];
1427 // e can be 0 if we had an empty entry or an error
1428 if (e == 0)
1429 return;
1430 if (e->start_col != c) {
1431 /* l s
1432 l ^ */
1433 error("impossible vertical span at row %1, column %2", r + 1, c + 1);
1434 }
1435 else {
1436 for (int i = c; i <= e->end_col; i++) {
1437 assert(entry[r][i] == 0);
1438 entry[r][i] = e;
1439 }
1440 e->end_row = r;
1441 }
1442}
1443
1444int find_decimal_point(const char *s, char decimal_point_char,
1445 const char *delim)
1446{
1447 if (s == 0 || *s == '\0')
1448 return -1;
1449 const char *p;
1450 int in_delim = 0; // is p within eqn delimiters?
1451 // tbl recognises \& even within eqn delimiters; I don't
1452 for (p = s; *p; p++)
1453 if (in_delim) {
1454 if (*p == delim[1])
1455 in_delim = 0;
1456 }
1457 else if (*p == delim[0])
1458 in_delim = 1;
1459 else if (p[0] == '\\' && p[1] == '&')
1460 return p - s;
1461 int possible_pos = -1;
1462 in_delim = 0;
1463 for (p = s; *p; p++)
1464 if (in_delim) {
1465 if (*p == delim[1])
1466 in_delim = 0;
1467 }
1468 else if (*p == delim[0])
1469 in_delim = 1;
1470 else if (p[0] == decimal_point_char && csdigit(p[1]))
1471 possible_pos = p - s;
1472 if (possible_pos >= 0)
1473 return possible_pos;
1474 in_delim = 0;
1475 for (p = s; *p; p++)
1476 if (in_delim) {
1477 if (*p == delim[1])
1478 in_delim = 0;
1479 }
1480 else if (*p == delim[0])
1481 in_delim = 1;
1482 else if (csdigit(*p))
1483 possible_pos = p + 1 - s;
1484 return possible_pos;
1485}
1486
1487void table::add_entry(int r, int c, const string &str, const entry_format *f,
1488 const char *fn, int ln)
1489{
1490 allocate(r);
1491 table_entry *e = 0;
1492 if (str == "\\_") {
4d3e9548 1493 e = new short_line_entry(this, f);
92d0a6a6
JR
1494 }
1495 else if (str == "\\=") {
4d3e9548 1496 e = new short_double_line_entry(this, f);
92d0a6a6
JR
1497 }
1498 else if (str == "_") {
1499 single_line_entry *lefte;
1500 if (c > 0 && entry[r][c-1] != 0 &&
1501 (lefte = entry[r][c-1]->to_single_line_entry()) != 0
1502 && lefte->start_row == r
1503 && lefte->mod->stagger == f->stagger) {
1504 lefte->end_col = c;
1505 entry[r][c] = lefte;
1506 }
1507 else
4d3e9548 1508 e = new single_line_entry(this, f);
92d0a6a6
JR
1509 }
1510 else if (str == "=") {
1511 double_line_entry *lefte;
1512 if (c > 0 && entry[r][c-1] != 0 &&
1513 (lefte = entry[r][c-1]->to_double_line_entry()) != 0
1514 && lefte->start_row == r
1515 && lefte->mod->stagger == f->stagger) {
1516 lefte->end_col = c;
1517 entry[r][c] = lefte;
1518 }
1519 else
4d3e9548 1520 e = new double_line_entry(this, f);
92d0a6a6
JR
1521 }
1522 else if (str == "\\^") {
1523 do_vspan(r, c);
1524 }
1525 else if (str.length() > 2 && str[0] == '\\' && str[1] == 'R') {
1526 if (str.search('\n') >= 0)
1527 error_with_file_and_line(fn, ln, "bad repeated character");
1528 else {
1529 char *s = str.substring(2, str.length() - 2).extract();
4d3e9548 1530 e = new repeated_char_entry(this, f, s);
92d0a6a6
JR
1531 }
1532 }
1533 else {
1534 int is_block = str.search('\n') >= 0;
1535 char *s;
1536 switch (f->type) {
1537 case FORMAT_SPAN:
1538 assert(str.empty());
1539 do_hspan(r, c);
1540 break;
1541 case FORMAT_LEFT:
1542 if (!str.empty()) {
1543 s = str.extract();
1544 if (is_block)
4d3e9548 1545 e = new left_block_entry(this, f, s);
92d0a6a6 1546 else
4d3e9548 1547 e = new left_text_entry(this, f, s);
92d0a6a6
JR
1548 }
1549 else
4d3e9548 1550 e = new empty_entry(this, f);
92d0a6a6
JR
1551 break;
1552 case FORMAT_CENTER:
1553 if (!str.empty()) {
1554 s = str.extract();
1555 if (is_block)
4d3e9548 1556 e = new center_block_entry(this, f, s);
92d0a6a6 1557 else
4d3e9548 1558 e = new center_text_entry(this, f, s);
92d0a6a6
JR
1559 }
1560 else
4d3e9548 1561 e = new empty_entry(this, f);
92d0a6a6
JR
1562 break;
1563 case FORMAT_RIGHT:
1564 if (!str.empty()) {
1565 s = str.extract();
1566 if (is_block)
4d3e9548 1567 e = new right_block_entry(this, f, s);
92d0a6a6 1568 else
4d3e9548 1569 e = new right_text_entry(this, f, s);
92d0a6a6
JR
1570 }
1571 else
4d3e9548 1572 e = new empty_entry(this, f);
92d0a6a6
JR
1573 break;
1574 case FORMAT_NUMERIC:
1575 if (!str.empty()) {
1576 s = str.extract();
1577 if (is_block) {
1578 error_with_file_and_line(fn, ln, "can't have numeric text block");
4d3e9548 1579 e = new left_block_entry(this, f, s);
92d0a6a6
JR
1580 }
1581 else {
1582 int pos = find_decimal_point(s, decimal_point_char, delim);
1583 if (pos < 0)
4d3e9548 1584 e = new center_text_entry(this, f, s);
92d0a6a6 1585 else
4d3e9548 1586 e = new numeric_text_entry(this, f, s, pos);
92d0a6a6
JR
1587 }
1588 }
1589 else
4d3e9548 1590 e = new empty_entry(this, f);
92d0a6a6
JR
1591 break;
1592 case FORMAT_ALPHABETIC:
1593 if (!str.empty()) {
1594 s = str.extract();
1595 if (is_block)
4d3e9548 1596 e = new alphabetic_block_entry(this, f, s);
92d0a6a6 1597 else
4d3e9548 1598 e = new alphabetic_text_entry(this, f, s);
92d0a6a6
JR
1599 }
1600 else
4d3e9548 1601 e = new empty_entry(this, f);
92d0a6a6
JR
1602 break;
1603 case FORMAT_VSPAN:
1604 do_vspan(r, c);
1605 break;
1606 case FORMAT_HLINE:
1607 if (str.length() != 0)
1608 error_with_file_and_line(fn, ln,
1609 "non-empty data entry for `_' format ignored");
4d3e9548 1610 e = new single_line_entry(this, f);
92d0a6a6
JR
1611 break;
1612 case FORMAT_DOUBLE_HLINE:
1613 if (str.length() != 0)
1614 error_with_file_and_line(fn, ln,
1615 "non-empty data entry for `=' format ignored");
4d3e9548 1616 e = new double_line_entry(this, f);
92d0a6a6
JR
1617 break;
1618 default:
1619 assert(0);
1620 }
1621 }
1622 if (e) {
1623 table_entry *preve = entry[r][c];
1624 if (preve) {
1625 /* c s
1626 ^ l */
1627 error_with_file_and_line(fn, ln, "row %1, column %2 already spanned",
1628 r + 1, c + 1);
1629 delete e;
1630 }
1631 else {
1632 e->input_lineno = ln;
1633 e->input_filename = fn;
1634 e->start_row = e->end_row = r;
1635 e->start_col = e->end_col = c;
1636 *entry_list_tailp = e;
1637 entry_list_tailp = &e->next;
1638 entry[r][c] = e;
1639 }
1640 }
1641}
1642
1643// add vertical lines for row r
1644
1645void table::add_vlines(int r, const char *v)
1646{
1647 allocate(r);
1648 for (int i = 0; i < ncolumns+1; i++)
1649 vline[r][i] = v[i];
1650}
1651
1652void table::check()
1653{
1654 table_entry *p = entry_list;
1655 int i, j;
1656 while (p) {
1657 for (i = p->start_row; i <= p->end_row; i++)
1658 for (j = p->start_col; j <= p->end_col; j++)
1659 assert(entry[i][j] == p);
1660 p = p->next;
1661 }
1662}
1663
1664void table::print()
1665{
1666 location_force_filename = 1;
1667 check();
1668 init_output();
1669 determine_row_type();
1670 compute_widths();
1671 if (!(flags & CENTER))
1672 prints(".if \\n[" SAVED_CENTER_REG "] \\{");
1673 prints(".in +(u;\\n[.l]-\\n[.i]-\\n[TW]/2>?-\\n[.i])\n"
1674 ".nr " SAVED_INDENT_REG " \\n[.i]\n");
1675 if (!(flags & CENTER))
1676 prints(".\\}\n");
1677 build_vrule_list();
1678 define_bottom_macro();
1679 do_top();
1680 for (int i = 0; i < nrows; i++)
1681 do_row(i);
1682 do_bottom();
1683}
1684
1685void table::determine_row_type()
1686{
1687 row_is_all_lines = new char[nrows];
1688 for (int i = 0; i < nrows; i++) {
1689 int had_single = 0;
1690 int had_double = 0;
1691 int had_non_line = 0;
1692 for (int c = 0; c < ncolumns; c++) {
1693 table_entry *e = entry[i][c];
1694 if (e != 0) {
1695 if (e->start_row == e->end_row) {
1696 int t = e->line_type();
1697 switch (t) {
1698 case -1:
1699 had_non_line = 1;
1700 break;
1701 case 0:
1702 // empty
1703 break;
1704 case 1:
1705 had_single = 1;
1706 break;
1707 case 2:
1708 had_double = 1;
1709 break;
1710 default:
1711 assert(0);
1712 }
1713 if (had_non_line)
1714 break;
1715 }
1716 c = e->end_col;
1717 }
1718 }
1719 if (had_non_line)
1720 row_is_all_lines[i] = 0;
1721 else if (had_double)
1722 row_is_all_lines[i] = 2;
1723 else if (had_single)
1724 row_is_all_lines[i] = 1;
1725 else
1726 row_is_all_lines[i] = 0;
1727 }
1728}
1729
4d3e9548
JL
1730int table::count_expand_columns()
1731{
1732 int count = 0;
1733 for (int i = 0; i < ncolumns; i++)
1734 if (expand[i])
1735 count++;
1736 return count;
1737}
1738
92d0a6a6
JR
1739void table::init_output()
1740{
1741 prints(".nr " COMPATIBLE_REG " \\n(.C\n"
1742 ".cp 0\n");
1743 if (linesize > 0)
1744 printfs(".nr " LINESIZE_REG " %1\n", as_string(linesize));
1745 else
1746 prints(".nr " LINESIZE_REG " \\n[.s]\n");
1747 if (!(flags & CENTER))
1748 prints(".nr " SAVED_CENTER_REG " \\n[.ce]\n");
465b256c
JR
1749 if (compatible_flag)
1750 prints(".ds " LEADER_REG " \\a\n");
92d0a6a6
JR
1751 prints(".de " RESET_MACRO_NAME "\n"
1752 ".ft \\n[.f]\n"
1753 ".ps \\n[.s]\n"
1754 ".vs \\n[.v]u\n"
1755 ".in \\n[.i]u\n"
1756 ".ll \\n[.l]u\n"
1757 ".ls \\n[.L]\n"
1758 ".ad \\n[.j]\n"
1759 ".ie \\n[.u] .fi\n"
1760 ".el .nf\n"
1761 ".ce \\n[.ce]\n"
1762 "..\n"
1763 ".nr " SAVED_INDENT_REG " \\n[.i]\n"
1764 ".nr " SAVED_FONT_REG " \\n[.f]\n"
1765 ".nr " SAVED_SIZE_REG " \\n[.s]\n"
1766 ".nr " SAVED_FILL_REG " \\n[.u]\n"
1767 ".nr T. 0\n"
1768 ".nr " CURRENT_ROW_REG " 0-1\n"
1769 ".nr " LAST_PASSED_ROW_REG " 0-1\n"
1770 ".nr " SECTION_DIVERSION_FLAG_REG " 0\n"
1771 ".ds " TRANSPARENT_STRING_NAME "\n"
1772 ".ds " QUOTE_STRING_NAME "\n"
1773 ".nr " NEED_BOTTOM_RULE_REG " 1\n"
1774 ".nr " SUPPRESS_BOTTOM_REG " 0\n"
1775 ".eo\n"
1776 ".de " REPEATED_MARK_MACRO "\n"
1777 ".mk \\$1\n"
1778 ".if !'\\n(.z'' \\!." REPEATED_MARK_MACRO " \"\\$1\"\n"
1779 "..\n"
1780 ".de " REPEATED_VPT_MACRO "\n"
1781 ".vpt \\$1\n"
1782 ".if !'\\n(.z'' \\!." REPEATED_VPT_MACRO " \"\\$1\"\n"
1783 "..\n");
1784 if (!(flags & NOKEEP))
1785 prints(".de " KEEP_MACRO_NAME "\n"
1786 ".if '\\n[.z]'' \\{.ds " QUOTE_STRING_NAME " \\\\\n"
1787 ".ds " TRANSPARENT_STRING_NAME " \\!\n"
1788 ".di " SECTION_DIVERSION_NAME "\n"
1789 ".nr " SECTION_DIVERSION_FLAG_REG " 1\n"
1790 ".in 0\n"
1791 ".\\}\n"
1792 "..\n"
1793 ".de " RELEASE_MACRO_NAME "\n"
1794 ".if \\n[" SECTION_DIVERSION_FLAG_REG "] \\{"
1795 ".di\n"
1796 ".in \\n[" SAVED_INDENT_REG "]u\n"
1797 ".nr " SAVED_DN_REG " \\n[dn]\n"
1798 ".ds " QUOTE_STRING_NAME "\n"
1799 ".ds " TRANSPARENT_STRING_NAME "\n"
1800 ".nr " SECTION_DIVERSION_FLAG_REG " 0\n"
1801 ".if \\n[.t]<=\\n[dn] \\{"
1802 ".nr T. 1\n"
1803 ".T#\n"
1804 ".nr " SUPPRESS_BOTTOM_REG " 1\n"
1805 ".sp \\n[.t]u\n"
1806 ".nr " SUPPRESS_BOTTOM_REG " 0\n"
1807 ".mk #T\n"
1808 ".\\}\n"
1809 ".if \\n[.t]<=\\n[" SAVED_DN_REG "] "
1810 /* Since we turn off traps, it won't get into an infinite loop
1811 when we try and print it; it will just go off the bottom of the
1812 page. */
1813 ".tm warning: page \\n%: table text block will not fit on one page\n"
1814 ".nf\n"
1815 ".ls 1\n"
1816 "." SECTION_DIVERSION_NAME "\n"
1817 ".ls\n"
1818 ".rm " SECTION_DIVERSION_NAME "\n"
1819 ".\\}\n"
1820 "..\n"
1821 ".nr " TABLE_DIVERSION_FLAG_REG " 0\n"
1822 ".de " TABLE_KEEP_MACRO_NAME "\n"
1823 ".if '\\n[.z]'' \\{"
1824 ".di " TABLE_DIVERSION_NAME "\n"
1825 ".nr " TABLE_DIVERSION_FLAG_REG " 1\n"
1826 ".\\}\n"
1827 "..\n"
1828 ".de " TABLE_RELEASE_MACRO_NAME "\n"
1829 ".if \\n[" TABLE_DIVERSION_FLAG_REG "] \\{.br\n"
1830 ".di\n"
1831 ".nr " SAVED_DN_REG " \\n[dn]\n"
1832 ".ne \\n[dn]u+\\n[.V]u\n"
1833 ".ie \\n[.t]<=\\n[" SAVED_DN_REG "] "
1834 ".tm error: page \\n%: table will not fit on one page; use .TS H/.TH with a supporting macro package\n"
1835 ".el \\{"
1836 ".in 0\n"
1837 ".ls 1\n"
1838 ".nf\n"
1839 "." TABLE_DIVERSION_NAME "\n"
1840 ".\\}\n"
1841 ".rm " TABLE_DIVERSION_NAME "\n"
1842 ".\\}\n"
1843 "..\n");
1844 prints(".ec\n"
1845 ".ce 0\n"
1846 ".nf\n");
1847}
1848
1849string block_width_reg(int r, int c)
1850{
1851 static char name[sizeof(BLOCK_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1852 sprintf(name, BLOCK_WIDTH_PREFIX "%d,%d", r, c);
1853 return string(name);
1854}
1855
1856string block_diversion_name(int r, int c)
1857{
1858 static char name[sizeof(BLOCK_DIVERSION_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1859 sprintf(name, BLOCK_DIVERSION_PREFIX "%d,%d", r, c);
1860 return string(name);
1861}
1862
1863string block_height_reg(int r, int c)
1864{
1865 static char name[sizeof(BLOCK_HEIGHT_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1866 sprintf(name, BLOCK_HEIGHT_PREFIX "%d,%d", r, c);
1867 return string(name);
1868}
1869
1870string span_width_reg(int start_col, int end_col)
1871{
1872 static char name[sizeof(SPAN_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1873 sprintf(name, SPAN_WIDTH_PREFIX "%d", start_col);
1874 if (end_col != start_col)
1875 sprintf(strchr(name, '\0'), ",%d", end_col);
1876 return string(name);
1877}
1878
1879string span_left_numeric_width_reg(int start_col, int end_col)
1880{
1881 static char name[sizeof(SPAN_LEFT_NUMERIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1882 sprintf(name, SPAN_LEFT_NUMERIC_WIDTH_PREFIX "%d", start_col);
1883 if (end_col != start_col)
1884 sprintf(strchr(name, '\0'), ",%d", end_col);
1885 return string(name);
1886}
1887
1888string span_right_numeric_width_reg(int start_col, int end_col)
1889{
1890 static char name[sizeof(SPAN_RIGHT_NUMERIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1891 sprintf(name, SPAN_RIGHT_NUMERIC_WIDTH_PREFIX "%d", start_col);
1892 if (end_col != start_col)
1893 sprintf(strchr(name, '\0'), ",%d", end_col);
1894 return string(name);
1895}
1896
1897string span_alphabetic_width_reg(int start_col, int end_col)
1898{
1899 static char name[sizeof(SPAN_ALPHABETIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1900 sprintf(name, SPAN_ALPHABETIC_WIDTH_PREFIX "%d", start_col);
1901 if (end_col != start_col)
1902 sprintf(strchr(name, '\0'), ",%d", end_col);
1903 return string(name);
1904}
1905
1906string column_separation_reg(int col)
1907{
1908 static char name[sizeof(COLUMN_SEPARATION_PREFIX)+INT_DIGITS];
1909 sprintf(name, COLUMN_SEPARATION_PREFIX "%d", col);
1910 return string(name);
1911}
1912
1913string row_start_reg(int row)
1914{
1915 static char name[sizeof(ROW_START_PREFIX)+INT_DIGITS];
1916 sprintf(name, ROW_START_PREFIX "%d", row);
1917 return string(name);
1918}
1919
1920string column_start_reg(int col)
1921{
1922 static char name[sizeof(COLUMN_START_PREFIX)+INT_DIGITS];
1923 sprintf(name, COLUMN_START_PREFIX "%d", col);
1924 return string(name);
1925}
1926
1927string column_end_reg(int col)
1928{
1929 static char name[sizeof(COLUMN_END_PREFIX)+INT_DIGITS];
1930 sprintf(name, COLUMN_END_PREFIX "%d", col);
1931 return string(name);
1932}
1933
1934string column_divide_reg(int col)
1935{
1936 static char name[sizeof(COLUMN_DIVIDE_PREFIX)+INT_DIGITS];
1937 sprintf(name, COLUMN_DIVIDE_PREFIX "%d", col);
1938 return string(name);
1939}
1940
1941string row_top_reg(int row)
1942{
1943 static char name[sizeof(ROW_TOP_PREFIX)+INT_DIGITS];
1944 sprintf(name, ROW_TOP_PREFIX "%d", row);
1945 return string(name);
1946}
1947
1948void init_span_reg(int start_col, int end_col)
1949{
1950 printfs(".nr %1 \\n(.H\n.nr %2 0\n.nr %3 0\n.nr %4 0\n",
1951 span_width_reg(start_col, end_col),
1952 span_alphabetic_width_reg(start_col, end_col),
1953 span_left_numeric_width_reg(start_col, end_col),
1954 span_right_numeric_width_reg(start_col, end_col));
1955}
1956
1957void compute_span_width(int start_col, int end_col)
1958{
1959 printfs(".nr %1 \\n[%1]>?(\\n[%2]+\\n[%3])\n"
1960 ".if \\n[%4] .nr %1 \\n[%1]>?(\\n[%4]+2n)\n",
1961 span_width_reg(start_col, end_col),
1962 span_left_numeric_width_reg(start_col, end_col),
1963 span_right_numeric_width_reg(start_col, end_col),
1964 span_alphabetic_width_reg(start_col, end_col));
1965}
1966
1967// Increase the widths of columns so that the width of any spanning entry
1968// is not greater than the sum of the widths of the columns that it spans.
1969// Ensure that the widths of columns remain equal.
1970
1971void table::divide_span(int start_col, int end_col)
1972{
1973 assert(end_col > start_col);
1974 printfs(".nr " NEEDED_REG " \\n[%1]-(\\n[%2]",
1975 span_width_reg(start_col, end_col),
1976 span_width_reg(start_col, start_col));
1977 int i;
1978 for (i = start_col + 1; i <= end_col; i++) {
1979 // The column separation may shrink with the expand option.
1980 if (!(flags & EXPAND))
1981 printfs("+%1n", as_string(column_separation[i - 1]));
1982 printfs("+\\n[%1]", span_width_reg(i, i));
1983 }
1984 prints(")\n");
1985 printfs(".nr " NEEDED_REG " \\n[" NEEDED_REG "]/%1\n",
1986 as_string(end_col - start_col + 1));
1987 prints(".if \\n[" NEEDED_REG "] \\{");
1988 for (i = start_col; i <= end_col; i++)
1989 printfs(".nr %1 +\\n[" NEEDED_REG "]\n",
1990 span_width_reg(i, i));
1991 int equal_flag = 0;
1992 for (i = start_col; i <= end_col && !equal_flag; i++)
4d3e9548 1993 if (equal[i] || expand[i])
92d0a6a6
JR
1994 equal_flag = 1;
1995 if (equal_flag) {
1996 for (i = 0; i < ncolumns; i++)
1997 if (i < start_col || i > end_col)
1998 printfs(".nr %1 +\\n[" NEEDED_REG "]\n",
1999 span_width_reg(i, i));
2000 }
2001 prints(".\\}\n");
2002}
2003
4d3e9548 2004void table::sum_columns(int start_col, int end_col, int do_expand)
92d0a6a6
JR
2005{
2006 assert(end_col > start_col);
4d3e9548
JL
2007 int i;
2008 for (i = start_col; i <= end_col; i++)
2009 if (expand[i])
2010 break;
2011 if (i > end_col) {
2012 if (do_expand)
2013 return;
2014 }
2015 else {
2016 if (!do_expand)
2017 return;
2018 }
92d0a6a6
JR
2019 printfs(".nr %1 \\n[%2]",
2020 span_width_reg(start_col, end_col),
2021 span_width_reg(start_col, start_col));
4d3e9548 2022 for (i = start_col + 1; i <= end_col; i++)
92d0a6a6
JR
2023 printfs("+(%1*\\n[" SEPARATION_FACTOR_REG "])+\\n[%2]",
2024 as_string(column_separation[i - 1]),
2025 span_width_reg(i, i));
2026 prints('\n');
2027}
2028
2029horizontal_span::horizontal_span(int sc, int ec, horizontal_span *p)
2030: next(p), start_col(sc), end_col(ec)
2031{
2032}
2033
2034void table::build_span_list()
2035{
2036 span_list = 0;
2037 table_entry *p = entry_list;
2038 while (p) {
2039 if (p->end_col != p->start_col) {
2040 horizontal_span *q;
2041 for (q = span_list; q; q = q->next)
2042 if (q->start_col == p->start_col
2043 && q->end_col == p->end_col)
2044 break;
2045 if (!q)
2046 span_list = new horizontal_span(p->start_col, p->end_col, span_list);
2047 }
2048 p = p->next;
2049 }
2050 // Now sort span_list primarily by order of end_row, and secondarily
2051 // by reverse order of start_row. This ensures that if we divide
2052 // spans using the order in span_list, we will get reasonable results.
2053 horizontal_span *unsorted = span_list;
2054 span_list = 0;
2055 while (unsorted) {
2056 horizontal_span **pp;
2057 for (pp = &span_list; *pp; pp = &(*pp)->next)
2058 if (unsorted->end_col < (*pp)->end_col
2059 || (unsorted->end_col == (*pp)->end_col
2060 && (unsorted->start_col > (*pp)->start_col)))
2061 break;
2062 horizontal_span *tem = unsorted->next;
2063 unsorted->next = *pp;
2064 *pp = unsorted;
2065 unsorted = tem;
2066 }
2067}
2068
4d3e9548 2069void table::compute_expand_width()
92d0a6a6 2070{
4d3e9548
JL
2071 int i;
2072 int colcount = count_expand_columns();
2073 prints(".nr " EXPAND_REG " \\n[.l]-\\n[.i]");
2074 for (i = 0; i < ncolumns; i++)
2075 if (!expand[i])
2076 printfs("-\\n[%1]", span_width_reg(i, i));
2077 if (total_separation)
2078 printfs("-%1n", as_string(total_separation));
2079 prints("\n");
2080 prints(".if \\n[" EXPAND_REG "]<0 \\{");
2081 entry_list->set_location();
2082 prints(".tm warning: around line \\n[.c]: table wider than line width\n"
2083 ".nr " EXPAND_REG " 0\n"
2084 ".\\}\n");
2085 if (colcount > 1)
2086 printfs(".nr " EXPAND_REG " \\n[" EXPAND_REG "]/%1\n",
2087 as_string(colcount));
2088 for (i = 0; i < ncolumns; i++)
2089 if (expand[i])
2090 printfs(".nr %1 \\n[%1]>?\\n[" EXPAND_REG "]\n", span_width_reg(i, i));
2091}
2092
2093void table::compute_total_separation()
2094{
2095 if (flags & (ALLBOX | BOX | DOUBLEBOX))
92d0a6a6
JR
2096 left_separation = right_separation = 1;
2097 else {
2098 for (int i = 0; i < nrows; i++) {
2099 if (vline[i][0] > 0)
2100 left_separation = 1;
2101 if (vline[i][ncolumns] > 0)
2102 right_separation = 1;
2103 }
2104 }
4d3e9548
JL
2105 total_separation = left_separation + right_separation;
2106 int i;
2107 for (i = 0; i < ncolumns - 1; i++)
2108 total_separation += column_separation[i];
2109}
2110
2111void table::compute_separation_factor()
2112{
2113 // Don't let the separation factor be negative.
2114 prints(".nr " SEPARATION_FACTOR_REG " \\n[.l]-\\n[.i]");
2115 for (int i = 0; i < ncolumns; i++)
2116 printfs("-\\n[%1]", span_width_reg(i, i));
2117 printfs("/%1\n", as_string(total_separation));
2118 prints(".ie \\n[" SEPARATION_FACTOR_REG "]<=0 \\{");
2119 entry_list->set_location();
2120 prints(".tm warning: around line \\n[.c]: column separation set to zero\n"
2121 ".nr " SEPARATION_FACTOR_REG " 0\n"
2122 ".\\}\n"
2123 ".el .if \\n[" SEPARATION_FACTOR_REG "]<1n \\{");
2124 entry_list->set_location();
2125 prints(".tm warning: around line \\n[.c]: table squeezed horizontally to fit line length\n"
2126 ".\\}\n");
92d0a6a6
JR
2127}
2128
2129void table::compute_column_positions()
2130{
2131 printfs(".nr %1 0\n", column_divide_reg(0));
2132 printfs(".nr %1 %2*\\n[" SEPARATION_FACTOR_REG "]\n",
2133 column_start_reg(0),
2134 as_string(left_separation));
2135 int i;
2136 for (i = 1;; i++) {
2137 printfs(".nr %1 \\n[%2]+\\n[%3]\n",
2138 column_end_reg(i-1),
2139 column_start_reg(i-1),
2140 span_width_reg(i-1, i-1));
2141 if (i >= ncolumns)
2142 break;
2143 printfs(".nr %1 \\n[%2]+(%3*\\n[" SEPARATION_FACTOR_REG "])\n",
2144 column_start_reg(i),
2145 column_end_reg(i-1),
2146 as_string(column_separation[i-1]));
2147 printfs(".nr %1 \\n[%2]+\\n[%3]/2\n",
2148 column_divide_reg(i),
2149 column_end_reg(i-1),
2150 column_start_reg(i));
2151 }
2152 printfs(".nr %1 \\n[%2]+(%3*\\n[" SEPARATION_FACTOR_REG "])\n",
2153 column_divide_reg(ncolumns),
2154 column_end_reg(i-1),
2155 as_string(right_separation));
2156 printfs(".nr TW \\n[%1]\n",
2157 column_divide_reg(ncolumns));
2158 if (flags & DOUBLEBOX) {
2159 printfs(".nr %1 +" DOUBLE_LINE_SEP "\n", column_divide_reg(0));
2160 printfs(".nr %1 -" DOUBLE_LINE_SEP "\n", column_divide_reg(ncolumns));
2161 }
2162}
2163
2164void table::make_columns_equal()
2165{
2166 int first = -1; // index of first equal column
2167 int i;
2168 for (i = 0; i < ncolumns; i++)
2169 if (equal[i]) {
2170 if (first < 0) {
2171 printfs(".nr %1 \\n[%1]", span_width_reg(i, i));
2172 first = i;
2173 }
2174 else
2175 printfs(">?\\n[%1]", span_width_reg(i, i));
2176 }
2177 if (first >= 0) {
2178 prints('\n');
2179 for (i = first + 1; i < ncolumns; i++)
2180 if (equal[i])
2181 printfs(".nr %1 \\n[%2]\n",
2182 span_width_reg(i, i),
2183 span_width_reg(first, first));
2184 }
2185}
2186
2187void table::compute_widths()
2188{
2189 build_span_list();
2190 int i;
2191 horizontal_span *p;
4d3e9548 2192 // These values get refined later.
92d0a6a6
JR
2193 prints(".nr " SEPARATION_FACTOR_REG " 1n\n");
2194 for (i = 0; i < ncolumns; i++) {
2195 init_span_reg(i, i);
2196 if (!minimum_width[i].empty())
4d3e9548 2197 printfs(".nr %1 (n;%2)\n", span_width_reg(i, i), minimum_width[i]);
92d0a6a6
JR
2198 }
2199 for (p = span_list; p; p = p->next)
2200 init_span_reg(p->start_col, p->end_col);
4d3e9548 2201 // Compute all field widths except for blocks.
92d0a6a6
JR
2202 table_entry *q;
2203 for (q = entry_list; q; q = q->next)
2204 if (!q->mod->zero_width)
2205 q->do_width();
4d3e9548 2206 // Compute all span widths, not handling blocks yet.
92d0a6a6
JR
2207 for (i = 0; i < ncolumns; i++)
2208 compute_span_width(i, i);
2209 for (p = span_list; p; p = p->next)
2210 compute_span_width(p->start_col, p->end_col);
4d3e9548 2211 // Making columns equal normally increases the width of some columns.
92d0a6a6
JR
2212 make_columns_equal();
2213 // Note that divide_span keeps equal width columns equal.
4d3e9548 2214 // This function might increase the width of some columns, too.
92d0a6a6
JR
2215 for (p = span_list; p; p = p->next)
2216 divide_span(p->start_col, p->end_col);
4d3e9548 2217 compute_total_separation();
92d0a6a6 2218 for (p = span_list; p; p = p->next)
4d3e9548
JL
2219 sum_columns(p->start_col, p->end_col, 0);
2220 // Now handle unexpanded blocks.
92d0a6a6
JR
2221 int had_spanning_block = 0;
2222 int had_equal_block = 0;
2223 for (q = entry_list; q; q = q->next)
2224 if (q->divert(ncolumns, minimum_width,
4d3e9548 2225 (flags & EXPAND) ? column_separation : 0, 0)) {
92d0a6a6
JR
2226 if (q->end_col > q->start_col)
2227 had_spanning_block = 1;
2228 for (i = q->start_col; i <= q->end_col && !had_equal_block; i++)
2229 if (equal[i])
2230 had_equal_block = 1;
2231 }
4d3e9548 2232 // Adjust widths.
92d0a6a6
JR
2233 if (had_equal_block)
2234 make_columns_equal();
2235 if (had_spanning_block)
2236 for (p = span_list; p; p = p->next)
2237 divide_span(p->start_col, p->end_col);
4d3e9548
JL
2238 compute_expand_width();
2239 if ((flags & EXPAND) && total_separation != 0) {
2240 compute_separation_factor();
2241 for (p = span_list; p; p = p->next)
2242 sum_columns(p->start_col, p->end_col, 0);
2243 }
2244 else {
2245 // Handle expanded blocks.
2246 for (p = span_list; p; p = p->next)
2247 sum_columns(p->start_col, p->end_col, 1);
2248 for (q = entry_list; q; q = q->next)
2249 if (q->divert(ncolumns, minimum_width, 0, 1)) {
2250 if (q->end_col > q->start_col)
2251 had_spanning_block = 1;
2252 }
2253 // Adjust widths again.
2254 if (had_spanning_block)
2255 for (p = span_list; p; p = p->next)
2256 divide_span(p->start_col, p->end_col);
2257 }
92d0a6a6
JR
2258 compute_column_positions();
2259}
2260
2261void table::print_single_hline(int r)
2262{
2263 prints(".vs " LINE_SEP ">?\\n[.V]u\n"
2264 ".ls 1\n"
2265 "\\v'" BODY_DEPTH "'"
2266 "\\s[\\n[" LINESIZE_REG "]]");
2267 if (r > nrows - 1)
2268 prints("\\D'l |\\n[TW]u 0'");
2269 else {
2270 int start_col = 0;
2271 for (;;) {
2272 while (start_col < ncolumns
2273 && entry[r][start_col] != 0
2274 && entry[r][start_col]->start_row != r)
2275 start_col++;
2276 int end_col;
2277 for (end_col = start_col;
2278 end_col < ncolumns
2279 && (entry[r][end_col] == 0
2280 || entry[r][end_col]->start_row == r);
2281 end_col++)
2282 ;
2283 if (end_col <= start_col)
2284 break;
2285 printfs("\\h'|\\n[%1]u",
2286 column_divide_reg(start_col));
2287 if ((r > 0 && vline[r-1][start_col] == 2)
2288 || (r < nrows && vline[r][start_col] == 2))
2289 prints("-" HALF_DOUBLE_LINE_SEP);
2290 prints("'");
2291 printfs("\\D'l |\\n[%1]u",
2292 column_divide_reg(end_col));
2293 if ((r > 0 && vline[r-1][end_col] == 2)
2294 || (r < nrows && vline[r][end_col] == 2))
2295 prints("+" HALF_DOUBLE_LINE_SEP);
2296 prints(" 0'");
2297 start_col = end_col;
2298 }
2299 }
2300 prints("\\s0\n");
2301 prints(".ls\n"
2302 ".vs\n");
2303}
2304
2305void table::print_double_hline(int r)
2306{
2307 prints(".vs " LINE_SEP "+" DOUBLE_LINE_SEP
2308 ">?\\n[.V]u\n"
2309 ".ls 1\n"
2310 "\\v'" BODY_DEPTH "'"
2311 "\\s[\\n[" LINESIZE_REG "]]");
2312 if (r > nrows - 1)
2313 prints("\\v'-" DOUBLE_LINE_SEP "'"
2314 "\\D'l |\\n[TW]u 0'"
2315 "\\v'" DOUBLE_LINE_SEP "'"
2316 "\\h'|0'"
2317 "\\D'l |\\n[TW]u 0'");
2318 else {
2319 int start_col = 0;
2320 for (;;) {
2321 while (start_col < ncolumns
2322 && entry[r][start_col] != 0
2323 && entry[r][start_col]->start_row != r)
2324 start_col++;
2325 int end_col;
2326 for (end_col = start_col;
2327 end_col < ncolumns
2328 && (entry[r][end_col] == 0
2329 || entry[r][end_col]->start_row == r);
2330 end_col++)
2331 ;
2332 if (end_col <= start_col)
2333 break;
2334 const char *left_adjust = 0;
2335 if ((r > 0 && vline[r-1][start_col] == 2)
2336 || (r < nrows && vline[r][start_col] == 2))
2337 left_adjust = "-" HALF_DOUBLE_LINE_SEP;
2338 const char *right_adjust = 0;
2339 if ((r > 0 && vline[r-1][end_col] == 2)
2340 || (r < nrows && vline[r][end_col] == 2))
2341 right_adjust = "+" HALF_DOUBLE_LINE_SEP;
2342 printfs("\\v'-" DOUBLE_LINE_SEP "'"
2343 "\\h'|\\n[%1]u",
2344 column_divide_reg(start_col));
2345 if (left_adjust)
2346 prints(left_adjust);
2347 prints("'");
2348 printfs("\\D'l |\\n[%1]u",
2349 column_divide_reg(end_col));
2350 if (right_adjust)
2351 prints(right_adjust);
2352 prints(" 0'");
2353 printfs("\\v'" DOUBLE_LINE_SEP "'"
2354 "\\h'|\\n[%1]u",
2355 column_divide_reg(start_col));
2356 if (left_adjust)
2357 prints(left_adjust);
2358 prints("'");
2359 printfs("\\D'l |\\n[%1]u",
2360 column_divide_reg(end_col));
2361 if (right_adjust)
2362 prints(right_adjust);
2363 prints(" 0'");
2364 start_col = end_col;
2365 }
2366 }
2367 prints("\\s0\n"
2368 ".ls\n"
2369 ".vs\n");
2370}
2371
2372void table::compute_vrule_top_adjust(int start_row, int col, string &result)
2373{
2374 if (row_is_all_lines[start_row] && start_row < nrows - 1) {
2375 if (row_is_all_lines[start_row] == 2)
2376 result = LINE_SEP ">?\\n[.V]u" "+" DOUBLE_LINE_SEP;
2377 else
2378 result = LINE_SEP ">?\\n[.V]u";
2379 start_row++;
2380 }
2381 else {
2382 result = "";
2383 if (start_row == 0)
2384 return;
2385 for (stuff *p = stuff_list; p && p->row <= start_row; p = p->next)
2386 if (p->row == start_row
2387 && (p->is_single_line() || p->is_double_line()))
2388 return;
2389 }
2390 int left = 0;
2391 if (col > 0) {
2392 table_entry *e = entry[start_row-1][col-1];
2393 if (e && e->start_row == e->end_row) {
2394 if (e->to_double_line_entry() != 0)
2395 left = 2;
2396 else if (e->to_single_line_entry() != 0)
2397 left = 1;
2398 }
2399 }
2400 int right = 0;
2401 if (col < ncolumns) {
2402 table_entry *e = entry[start_row-1][col];
2403 if (e && e->start_row == e->end_row) {
2404 if (e->to_double_line_entry() != 0)
2405 right = 2;
2406 else if (e->to_single_line_entry() != 0)
2407 right = 1;
2408 }
2409 }
2410 if (row_is_all_lines[start_row-1] == 0) {
2411 if (left > 0 || right > 0) {
2412 result += "-" BODY_DEPTH "-" BAR_HEIGHT;
2413 if ((left == 2 && right != 2) || (right == 2 && left != 2))
2414 result += "-" HALF_DOUBLE_LINE_SEP;
2415 else if (left == 2 && right == 2)
2416 result += "+" HALF_DOUBLE_LINE_SEP;
2417 }
2418 }
2419 else if (row_is_all_lines[start_row-1] == 2) {
2420 if ((left == 2 && right != 2) || (right == 2 && left != 2))
2421 result += "-" DOUBLE_LINE_SEP;
2422 else if (left == 1 || right == 1)
2423 result += "-" HALF_DOUBLE_LINE_SEP;
2424 }
2425}
2426
2427void table::compute_vrule_bot_adjust(int end_row, int col, string &result)
2428{
2429 if (row_is_all_lines[end_row] && end_row > 0) {
2430 end_row--;
2431 result = "";
2432 }
2433 else {
2434 stuff *p;
2435 for (p = stuff_list; p && p->row < end_row + 1; p = p->next)
2436 ;
2437 if (p && p->row == end_row + 1 && p->is_double_line()) {
2438 result = "-" DOUBLE_LINE_SEP;
2439 return;
2440 }
2441 if ((p != 0 && p->row == end_row + 1)
2442 || end_row == nrows - 1) {
2443 result = "";
2444 return;
2445 }
2446 if (row_is_all_lines[end_row+1] == 1)
2447 result = LINE_SEP;
2448 else if (row_is_all_lines[end_row+1] == 2)
2449 result = LINE_SEP "+" DOUBLE_LINE_SEP;
2450 else
2451 result = "";
2452 }
2453 int left = 0;
2454 if (col > 0) {
2455 table_entry *e = entry[end_row+1][col-1];
2456 if (e && e->start_row == e->end_row) {
2457 if (e->to_double_line_entry() != 0)
2458 left = 2;
2459 else if (e->to_single_line_entry() != 0)
2460 left = 1;
2461 }
2462 }
2463 int right = 0;
2464 if (col < ncolumns) {
2465 table_entry *e = entry[end_row+1][col];
2466 if (e && e->start_row == e->end_row) {
2467 if (e->to_double_line_entry() != 0)
2468 right = 2;
2469 else if (e->to_single_line_entry() != 0)
2470 right = 1;
2471 }
2472 }
2473 if (row_is_all_lines[end_row+1] == 0) {
2474 if (left > 0 || right > 0) {
2475 result = "1v-" BODY_DEPTH "-" BAR_HEIGHT;
2476 if ((left == 2 && right != 2) || (right == 2 && left != 2))
2477 result += "+" HALF_DOUBLE_LINE_SEP;
2478 else if (left == 2 && right == 2)
2479 result += "-" HALF_DOUBLE_LINE_SEP;
2480 }
2481 }
2482 else if (row_is_all_lines[end_row+1] == 2) {
2483 if (left == 2 && right == 2)
2484 result += "-" DOUBLE_LINE_SEP;
2485 else if (left != 2 && right != 2 && (left == 1 || right == 1))
2486 result += "-" HALF_DOUBLE_LINE_SEP;
2487 }
2488}
2489
4d3e9548
JL
2490void table::add_vertical_rule(int start_row, int end_row,
2491 int col, int is_double)
92d0a6a6
JR
2492{
2493 vrule_list = new vertical_rule(start_row, end_row, col, is_double,
2494 vrule_list);
2495 compute_vrule_top_adjust(start_row, col, vrule_list->top_adjust);
2496 compute_vrule_bot_adjust(end_row, col, vrule_list->bot_adjust);
2497}
2498
2499void table::build_vrule_list()
2500{
2501 int col;
2502 if (flags & ALLBOX) {
2503 for (col = 1; col < ncolumns; col++) {
2504 int start_row = 0;
2505 for (;;) {
2506 while (start_row < nrows && vline_spanned(start_row, col))
2507 start_row++;
2508 if (start_row >= nrows)
2509 break;
2510 int end_row = start_row;
2511 while (end_row < nrows && !vline_spanned(end_row, col))
2512 end_row++;
2513 end_row--;
2514 add_vertical_rule(start_row, end_row, col, 0);
2515 start_row = end_row + 1;
2516 }
2517 }
2518 }
4d3e9548 2519 if (flags & (BOX | ALLBOX | DOUBLEBOX)) {
92d0a6a6
JR
2520 add_vertical_rule(0, nrows - 1, 0, 0);
2521 add_vertical_rule(0, nrows - 1, ncolumns, 0);
2522 }
2523 for (int end_row = 0; end_row < nrows; end_row++)
2524 for (col = 0; col < ncolumns+1; col++)
2525 if (vline[end_row][col] > 0
2526 && !vline_spanned(end_row, col)
2527 && (end_row == nrows - 1
2528 || vline[end_row+1][col] != vline[end_row][col]
2529 || vline_spanned(end_row+1, col))) {
2530 int start_row;
2531 for (start_row = end_row - 1;
2532 start_row >= 0
2533 && vline[start_row][col] == vline[end_row][col]
2534 && !vline_spanned(start_row, col);
2535 start_row--)
2536 ;
2537 start_row++;
2538 add_vertical_rule(start_row, end_row, col, vline[end_row][col] > 1);
2539 }
2540 for (vertical_rule *p = vrule_list; p; p = p->next)
2541 if (p->is_double)
2542 for (int r = p->start_row; r <= p->end_row; r++) {
2543 if (p->col > 0 && entry[r][p->col-1] != 0
2544 && entry[r][p->col-1]->end_col == p->col-1) {
2545 int is_corner = r == p->start_row || r == p->end_row;
2546 entry[r][p->col-1]->note_double_vrule_on_right(is_corner);
2547 }
2548 if (p->col < ncolumns && entry[r][p->col] != 0
2549 && entry[r][p->col]->start_col == p->col) {
2550 int is_corner = r == p->start_row || r == p->end_row;
2551 entry[r][p->col]->note_double_vrule_on_left(is_corner);
2552 }
2553 }
2554}
2555
2556void table::define_bottom_macro()
2557{
2558 prints(".eo\n"
2559 ".de T#\n"
2560 ".if !\\n[" SUPPRESS_BOTTOM_REG "] \\{"
2561 "." REPEATED_VPT_MACRO " 0\n"
2562 ".mk " SAVED_VERTICAL_POS_REG "\n");
4d3e9548 2563 if (flags & (BOX | ALLBOX | DOUBLEBOX)) {
92d0a6a6
JR
2564 prints(".if \\n[T.]&\\n[" NEED_BOTTOM_RULE_REG "] \\{");
2565 print_single_hline(0);
2566 prints(".\\}\n");
2567 }
2568 prints(".ls 1\n");
2569 for (vertical_rule *p = vrule_list; p; p = p->next)
2570 p->contribute_to_bottom_macro(this);
2571 if (flags & DOUBLEBOX)
2572 prints(".if \\n[T.] \\{.vs " DOUBLE_LINE_SEP ">?\\n[.V]u\n"
2573 "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]"
2574 "\\D'l \\n[TW]u 0'\\s0\n"
2575 ".vs\n"
2576 ".\\}\n"
2577 ".if \\n[" LAST_PASSED_ROW_REG "]>=0 "
2578 ".nr " TOP_REG " \\n[#T]-" DOUBLE_LINE_SEP "\n"
2579 ".sp -1\n"
2580 "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]"
2581 "\\D'l 0 |\\n[" TOP_REG "]u-1v'\\s0\n"
2582 ".sp -1\n"
2583 "\\v'" BODY_DEPTH "'\\h'|\\n[TW]u'\\s[\\n[" LINESIZE_REG "]]"
2584 "\\D'l 0 |\\n[" TOP_REG "]u-1v'\\s0\n");
2585 prints(".ls\n");
2586 prints(".nr " LAST_PASSED_ROW_REG " \\n[" CURRENT_ROW_REG "]\n"
2587 ".sp |\\n[" SAVED_VERTICAL_POS_REG "]u\n"
2588 "." REPEATED_VPT_MACRO " 1\n"
2589 ".\\}\n"
2590 "..\n"
2591 ".ec\n");
2592}
2593
2594// is the vertical line before column c in row r horizontally spanned?
2595
2596int table::vline_spanned(int r, int c)
2597{
2598 assert(r >= 0 && r < nrows && c >= 0 && c < ncolumns + 1);
2599 return (c != 0 && c != ncolumns && entry[r][c] != 0
2600 && entry[r][c]->start_col != c
2601 // horizontally spanning lines don't count
2602 && entry[r][c]->to_double_line_entry() == 0
2603 && entry[r][c]->to_single_line_entry() == 0);
2604}
2605
2606int table::row_begins_section(int r)
2607{
2608 assert(r >= 0 && r < nrows);
2609 for (int i = 0; i < ncolumns; i++)
2610 if (entry[r][i] && entry[r][i]->start_row != r)
2611 return 0;
2612 return 1;
2613}
2614
2615int table::row_ends_section(int r)
2616{
2617 assert(r >= 0 && r < nrows);
2618 for (int i = 0; i < ncolumns; i++)
2619 if (entry[r][i] && entry[r][i]->end_row != r)
2620 return 0;
2621 return 1;
2622}
2623
2624void table::do_row(int r)
2625{
2626 if (!(flags & NOKEEP) && row_begins_section(r))
2627 prints("." KEEP_MACRO_NAME "\n");
2628 int had_line = 0;
2629 stuff *p;
2630 for (p = stuff_list; p && p->row < r; p = p->next)
2631 ;
2632 for (stuff *p1 = p; p1 && p1->row == r; p1 = p1->next)
2633 if (!p1->printed && (p1->is_single_line() || p1->is_double_line())) {
2634 had_line = 1;
2635 break;
2636 }
2637 if (!had_line && !row_is_all_lines[r])
2638 printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r));
2639 had_line = 0;
2640 for (; p && p->row == r; p = p->next)
2641 if (!p->printed) {
2642 p->print(this);
2643 if (!had_line && (p->is_single_line() || p->is_double_line())) {
2644 printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r));
2645 had_line = 1;
2646 }
2647 }
4d3e9548 2648 // change the row *after* printing the stuff list (which might contain .TH)
92d0a6a6
JR
2649 printfs("\\*[" TRANSPARENT_STRING_NAME "].nr " CURRENT_ROW_REG " %1\n",
2650 as_string(r));
2651 if (!had_line && row_is_all_lines[r])
2652 printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r));
2653 // we might have had a .TH, for example, since we last tried
2654 if (!(flags & NOKEEP) && row_begins_section(r))
2655 prints("." KEEP_MACRO_NAME "\n");
2656 printfs(".mk %1\n", row_start_reg(r));
2657 prints(".mk " BOTTOM_REG "\n"
2658 "." REPEATED_VPT_MACRO " 0\n");
2659 int c;
2660 int row_is_blank = 1;
2661 int first_start_row = r;
2662 for (c = 0; c < ncolumns; c++) {
2663 table_entry *e = entry[r][c];
2664 if (e) {
2665 if (e->end_row == r) {
2666 e->do_depth();
2667 if (e->start_row < first_start_row)
2668 first_start_row = e->start_row;
2669 row_is_blank = 0;
2670 }
2671 c = e->end_col;
2672 }
2673 }
2674 if (row_is_blank)
2675 prints(".nr " BOTTOM_REG " +1v\n");
2676 if (row_is_all_lines[r]) {
2677 prints(".vs " LINE_SEP);
2678 if (row_is_all_lines[r] == 2)
2679 prints("+" DOUBLE_LINE_SEP);
2680 prints(">?\\n[.V]u\n.ls 1\n");
2681 prints("\\&");
2682 prints("\\v'" BODY_DEPTH);
2683 if (row_is_all_lines[r] == 2)
2684 prints("-" HALF_DOUBLE_LINE_SEP);
2685 prints("'");
2686 for (c = 0; c < ncolumns; c++) {
2687 table_entry *e = entry[r][c];
2688 if (e) {
2689 if (e->end_row == e->start_row)
2690 e->to_simple_entry()->simple_print(1);
2691 c = e->end_col;
2692 }
2693 }
2694 prints("\n");
2695 prints(".ls\n"
2696 ".vs\n");
2697 prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n");
2698 printfs(".sp |\\n[%1]u\n", row_start_reg(r));
2699 }
2700 for (int i = row_is_all_lines[r] ? r - 1 : r;
2701 i >= first_start_row;
2702 i--) {
2703 simple_entry *first = 0;
2704 for (c = 0; c < ncolumns; c++) {
2705 table_entry *e = entry[r][c];
2706 if (e) {
2707 if (e->end_row == r && e->start_row == i) {
2708 simple_entry *simple = e->to_simple_entry();
2709 if (simple) {
2710 if (!first) {
2711 prints(".ta");
2712 first = simple;
2713 }
2714 simple->add_tab();
2715 }
2716 }
2717 c = e->end_col;
2718 }
2719 }
2720 if (first) {
2721 prints('\n');
2722 first->position_vertically();
2723 first->set_location();
2724 prints("\\&");
2725 first->simple_print(0);
2726 for (c = first->end_col + 1; c < ncolumns; c++) {
2727 table_entry *e = entry[r][c];
2728 if (e) {
2729 if (e->end_row == r && e->start_row == i) {
2730 simple_entry *simple = e->to_simple_entry();
465b256c
JR
2731 if (simple) {
2732 if (e->end_row != e->start_row) {
2733 prints('\n');
2734 simple->position_vertically();
2735 prints("\\&");
2736 }
92d0a6a6 2737 simple->simple_print(0);
465b256c 2738 }
92d0a6a6
JR
2739 }
2740 c = e->end_col;
2741 }
2742 }
2743 prints('\n');
2744 prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n");
2745 printfs(".sp |\\n[%1]u\n", row_start_reg(r));
2746 }
2747 }
2748 for (c = 0; c < ncolumns; c++) {
2749 table_entry *e = entry[r][c];
2750 if (e) {
2751 if (e->end_row == r && e->to_simple_entry() == 0) {
2752 e->position_vertically();
2753 e->print();
2754 prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n");
2755 printfs(".sp |\\n[%1]u\n", row_start_reg(r));
2756 }
2757 c = e->end_col;
2758 }
2759 }
2760 prints("." REPEATED_VPT_MACRO " 1\n"
2761 ".sp |\\n[" BOTTOM_REG "]u\n"
2762 "\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG " 1\n");
2763 if (r != nrows - 1 && (flags & ALLBOX)) {
2764 print_single_hline(r + 1);
2765 prints("\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG " 0\n");
2766 }
2767 if (r != nrows - 1) {
2768 if (p && p->row == r + 1
2769 && (p->is_single_line() || p->is_double_line())) {
2770 p->print(this);
2771 prints("\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG
2772 " 0\n");
2773 }
2774 int printed_one = 0;
2775 for (vertical_rule *vr = vrule_list; vr; vr = vr->next)
2776 if (vr->end_row == r) {
2777 if (!printed_one) {
2778 prints("." REPEATED_VPT_MACRO " 0\n");
2779 printed_one = 1;
2780 }
2781 vr->print();
2782 }
2783 if (printed_one)
2784 prints("." REPEATED_VPT_MACRO " 1\n");
2785 if (!(flags & NOKEEP) && row_ends_section(r))
2786 prints("." RELEASE_MACRO_NAME "\n");
2787 }
2788}
2789
2790void table::do_top()
2791{
2792 prints(".fc \002\003\n");
4d3e9548 2793 if (!(flags & NOKEEP) && (flags & (BOX | DOUBLEBOX | ALLBOX)))
92d0a6a6
JR
2794 prints("." TABLE_KEEP_MACRO_NAME "\n");
2795 if (flags & DOUBLEBOX) {
2796 prints(".ls 1\n"
2797 ".vs " LINE_SEP ">?\\n[.V]u\n"
2798 "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]\\D'l \\n[TW]u 0'\\s0\n"
2799 ".vs\n"
2800 "." REPEATED_MARK_MACRO " " TOP_REG "\n"
2801 ".vs " DOUBLE_LINE_SEP ">?\\n[.V]u\n");
2802 printfs("\\v'" BODY_DEPTH "'"
2803 "\\s[\\n[" LINESIZE_REG "]]"
2804 "\\h'\\n[%1]u'"
2805 "\\D'l |\\n[%2]u 0'"
2806 "\\s0"
2807 "\n",
2808 column_divide_reg(0),
2809 column_divide_reg(ncolumns));
2810 prints(".ls\n"
2811 ".vs\n");
2812 }
4d3e9548 2813 else if (flags & (ALLBOX | BOX)) {
92d0a6a6
JR
2814 print_single_hline(0);
2815 }
2816 //printfs(".mk %1\n", row_top_reg(0));
2817}
2818
2819void table::do_bottom()
2820{
2821 // print stuff after last row
2822 for (stuff *p = stuff_list; p; p = p->next)
2823 if (p->row > nrows - 1)
2824 p->print(this);
2825 if (!(flags & NOKEEP))
2826 prints("." RELEASE_MACRO_NAME "\n");
2827 printfs(".mk %1\n", row_top_reg(nrows));
2828 prints(".nr " NEED_BOTTOM_RULE_REG " 1\n"
2829 ".nr T. 1\n"
2830 ".T#\n");
4d3e9548 2831 if (!(flags & NOKEEP) && (flags & (BOX | DOUBLEBOX | ALLBOX)))
92d0a6a6
JR
2832 prints("." TABLE_RELEASE_MACRO_NAME "\n");
2833 if (flags & DOUBLEBOX)
2834 prints(".sp " DOUBLE_LINE_SEP "\n");
2835 prints("." RESET_MACRO_NAME "\n"
2836 ".fc\n"
2837 ".cp \\n(" COMPATIBLE_REG "\n");
2838}
2839
2840int table::get_nrows()
2841{
2842 return nrows;
2843}
2844
2845const char *last_filename = 0;
2846
2847void set_troff_location(const char *fn, int ln)
2848{
2849 if (!location_force_filename && last_filename != 0
2850 && strcmp(fn, last_filename) == 0)
2851 printfs(".lf %1\n", as_string(ln));
2852 else {
2853 printfs(".lf %1 %2\n", as_string(ln), fn);
2854 last_filename = fn;
2855 location_force_filename = 0;
2856 }
2857}
2858
2859void printfs(const char *s, const string &arg1, const string &arg2,
2860 const string &arg3, const string &arg4, const string &arg5)
2861{
2862 if (s) {
2863 char c;
2864 while ((c = *s++) != '\0') {
2865 if (c == '%') {
2866 switch (*s++) {
2867 case '1':
2868 prints(arg1);
2869 break;
2870 case '2':
2871 prints(arg2);
2872 break;
2873 case '3':
2874 prints(arg3);
2875 break;
2876 case '4':
2877 prints(arg4);
2878 break;
2879 case '5':
2880 prints(arg5);
2881 break;
2882 case '6':
2883 case '7':
2884 case '8':
2885 case '9':
2886 break;
2887 case '%':
2888 prints('%');
2889 break;
2890 default:
2891 assert(0);
2892 }
2893 }
2894 else
2895 prints(c);
2896 }
2897 }
2898}
2899