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