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