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