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