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