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