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