1114402Sru// -*- C++ -*-
2151497Sru/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2003, 2004
3114402Sru   Free Software Foundation, Inc.
4114402Sru     Written by James Clark (jjc@jclark.com)
5114402Sru
6114402SruThis file is part of groff.
7114402Sru
8114402Srugroff is free software; you can redistribute it and/or modify it under
9114402Sruthe terms of the GNU General Public License as published by the Free
10114402SruSoftware Foundation; either version 2, or (at your option) any later
11114402Sruversion.
12114402Sru
13114402Srugroff is distributed in the hope that it will be useful, but WITHOUT ANY
14114402SruWARRANTY; without even the implied warranty of MERCHANTABILITY or
15114402SruFITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16114402Srufor more details.
17114402Sru
18114402SruYou should have received a copy of the GNU General Public License along
19114402Sruwith groff; see the file COPYING.  If not, write to the Free Software
20151497SruFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
21114402Sru
22114402Sru#include "table.h"
23114402Sru
24114402Sru#define BAR_HEIGHT ".25m"
25114402Sru#define DOUBLE_LINE_SEP "2p"
26114402Sru#define HALF_DOUBLE_LINE_SEP "1p"
27114402Sru#define LINE_SEP "2p"
28114402Sru#define BODY_DEPTH ".25m"
29114402Sru
30114402Sruconst int DEFAULT_COLUMN_SEPARATION = 3;
31114402Sru
32114402Sru#define DELIMITER_CHAR "\\[tbl]"
33114402Sru#define SEPARATION_FACTOR_REG PREFIX "sep"
34114402Sru#define BOTTOM_REG PREFIX "bot"
35114402Sru#define RESET_MACRO_NAME PREFIX "init"
36114402Sru#define LINESIZE_REG PREFIX "lps"
37114402Sru#define TOP_REG PREFIX "top"
38114402Sru#define CURRENT_ROW_REG PREFIX "crow"
39114402Sru#define LAST_PASSED_ROW_REG PREFIX "passed"
40114402Sru#define TRANSPARENT_STRING_NAME PREFIX "trans"
41114402Sru#define QUOTE_STRING_NAME PREFIX "quote"
42114402Sru#define SECTION_DIVERSION_NAME PREFIX "section"
43114402Sru#define SECTION_DIVERSION_FLAG_REG PREFIX "sflag"
44114402Sru#define SAVED_VERTICAL_POS_REG PREFIX "vert"
45114402Sru#define NEED_BOTTOM_RULE_REG PREFIX "brule"
46114402Sru#define KEEP_MACRO_NAME PREFIX "keep"
47114402Sru#define RELEASE_MACRO_NAME PREFIX "release"
48114402Sru#define SAVED_FONT_REG PREFIX "fnt"
49114402Sru#define SAVED_SIZE_REG PREFIX "sz"
50114402Sru#define SAVED_FILL_REG PREFIX "fll"
51114402Sru#define SAVED_INDENT_REG PREFIX "ind"
52114402Sru#define SAVED_CENTER_REG PREFIX "cent"
53114402Sru#define TABLE_DIVERSION_NAME PREFIX "table"
54114402Sru#define TABLE_DIVERSION_FLAG_REG PREFIX "tflag"
55114402Sru#define TABLE_KEEP_MACRO_NAME PREFIX "tkeep"
56114402Sru#define TABLE_RELEASE_MACRO_NAME PREFIX "trelease"
57114402Sru#define NEEDED_REG PREFIX "needed"
58114402Sru#define REPEATED_MARK_MACRO PREFIX "rmk"
59114402Sru#define REPEATED_VPT_MACRO PREFIX "rvpt"
60114402Sru#define SUPPRESS_BOTTOM_REG PREFIX "supbot"
61114402Sru#define SAVED_DN_REG PREFIX "dn"
62114402Sru
63114402Sru// this must be one character
64114402Sru#define COMPATIBLE_REG PREFIX "c"
65114402Sru
66151497Sru#define LEADER_REG PREFIX LEADER
67151497Sru
68114402Sru#define BLOCK_WIDTH_PREFIX PREFIX "tbw"
69114402Sru#define BLOCK_DIVERSION_PREFIX PREFIX "tbd"
70114402Sru#define BLOCK_HEIGHT_PREFIX PREFIX "tbh"
71114402Sru#define SPAN_WIDTH_PREFIX PREFIX "w"
72114402Sru#define SPAN_LEFT_NUMERIC_WIDTH_PREFIX PREFIX "lnw"
73114402Sru#define SPAN_RIGHT_NUMERIC_WIDTH_PREFIX PREFIX "rnw"
74114402Sru#define SPAN_ALPHABETIC_WIDTH_PREFIX PREFIX "aw"
75114402Sru#define COLUMN_SEPARATION_PREFIX PREFIX "cs"
76114402Sru#define ROW_START_PREFIX PREFIX "rs"
77114402Sru#define COLUMN_START_PREFIX PREFIX "cl"
78114402Sru#define COLUMN_END_PREFIX PREFIX "ce"
79114402Sru#define COLUMN_DIVIDE_PREFIX PREFIX "cd"
80114402Sru#define ROW_TOP_PREFIX PREFIX "rt"
81114402Sru
82114402Srustring block_width_reg(int r, int c);
83114402Srustring block_diversion_name(int r, int c);
84114402Srustring block_height_reg(int r, int c);
85114402Srustring span_width_reg(int start_col, int end_col);
86114402Srustring span_left_numeric_width_reg(int start_col, int end_col);
87114402Srustring span_right_numeric_width_reg(int start_col, int end_col);
88114402Srustring span_alphabetic_width_reg(int start_col, int end_col);
89114402Srustring column_separation_reg(int col);
90114402Srustring row_start_reg(int r);
91114402Srustring column_start_reg(int c);
92114402Srustring column_end_reg(int c);
93114402Srustring column_divide_reg(int c);
94114402Srustring row_top_reg(int r);
95114402Sru
96114402Sruvoid set_inline_modifier(const entry_modifier *);
97114402Sruvoid restore_inline_modifier(const entry_modifier *m);
98114402Sruvoid set_modifier(const entry_modifier *);
99114402Sruint find_decimal_point(const char *s, char decimal_point_char,
100114402Sru		       const char *delim);
101114402Sru
102114402Srustring an_empty_string;
103114402Sruint location_force_filename = 0;
104114402Sru
105114402Sruvoid printfs(const char *,
106114402Sru	     const string &arg1 = an_empty_string,
107114402Sru	     const string &arg2 = an_empty_string,
108114402Sru	     const string &arg3 = an_empty_string,
109114402Sru	     const string &arg4 = an_empty_string,
110114402Sru	     const string &arg5 = an_empty_string);
111114402Sru
112114402Sruvoid prints(const string &);
113114402Sru
114114402Sruinline void prints(char c)
115114402Sru{
116114402Sru  putchar(c);
117114402Sru}
118114402Sru
119114402Sruinline void prints(const char *s)
120114402Sru{
121114402Sru  fputs(s, stdout);
122114402Sru}
123114402Sru
124114402Sruvoid prints(const string &s)
125114402Sru{
126114402Sru  if (!s.empty())
127114402Sru    fwrite(s.contents(), 1, s.length(), stdout);
128114402Sru}
129114402Sru
130114402Srustruct horizontal_span {
131114402Sru  horizontal_span *next;
132151497Sru  int start_col;
133151497Sru  int end_col;
134114402Sru  horizontal_span(int, int, horizontal_span *);
135114402Sru};
136114402Sru
137151497Sruclass single_line_entry;
138151497Sruclass double_line_entry;
139151497Sruclass simple_entry;
140114402Sru
141114402Sruclass table_entry {
142114402Srufriend class table;
143114402Sru  table_entry *next;
144114402Sru  int input_lineno;
145114402Sru  const char *input_filename;
146114402Sruprotected:
147114402Sru  int start_row;
148114402Sru  int end_row;
149151497Sru  int start_col;
150151497Sru  int end_col;
151114402Sru  const entry_modifier *mod;
152114402Srupublic:
153114402Sru  void set_location();
154114402Sru  table_entry(const entry_modifier *);
155114402Sru  virtual ~table_entry();
156114402Sru  virtual int divert(int ncols, const string *mw, int *sep);
157114402Sru  virtual void do_width();
158114402Sru  virtual void do_depth();
159114402Sru  virtual void print() = 0;
160114402Sru  virtual void position_vertically() = 0;
161114402Sru  virtual single_line_entry *to_single_line_entry();
162114402Sru  virtual double_line_entry *to_double_line_entry();
163114402Sru  virtual simple_entry *to_simple_entry();
164114402Sru  virtual int line_type();
165114402Sru  virtual void note_double_vrule_on_right(int);
166114402Sru  virtual void note_double_vrule_on_left(int);
167114402Sru};
168114402Sru
169114402Sruclass simple_entry : public table_entry {
170114402Srupublic:
171114402Sru  simple_entry(const entry_modifier *);
172114402Sru  void print();
173114402Sru  void position_vertically();
174114402Sru  simple_entry *to_simple_entry();
175114402Sru  virtual void add_tab();
176114402Sru  virtual void simple_print(int);
177114402Sru};
178114402Sru
179114402Sruclass empty_entry : public simple_entry {
180114402Srupublic:
181114402Sru  empty_entry(const entry_modifier *);
182114402Sru  int line_type();
183114402Sru};
184114402Sru
185114402Sruclass text_entry : public simple_entry {
186114402Sruprotected:
187114402Sru  char *contents;
188114402Sru  void print_contents();
189114402Srupublic:
190114402Sru  text_entry(char *, const entry_modifier *);
191114402Sru  ~text_entry();
192114402Sru};
193114402Sru
194114402Sruvoid text_entry::print_contents()
195114402Sru{
196114402Sru  set_inline_modifier(mod);
197114402Sru  prints(contents);
198114402Sru  restore_inline_modifier(mod);
199114402Sru}
200114402Sru
201114402Sruclass repeated_char_entry : public text_entry {
202114402Srupublic:
203114402Sru  repeated_char_entry(char *s, const entry_modifier *m);
204114402Sru  void simple_print(int);
205114402Sru};
206114402Sru
207114402Sruclass simple_text_entry : public text_entry {
208114402Srupublic:
209114402Sru  simple_text_entry(char *s, const entry_modifier *m);
210114402Sru  void do_width();
211114402Sru};
212114402Sru
213114402Sruclass left_text_entry : public simple_text_entry {
214114402Srupublic:
215114402Sru  left_text_entry(char *s, const entry_modifier *m);
216114402Sru  void simple_print(int);
217114402Sru  void add_tab();
218114402Sru};
219114402Sru
220114402Sruclass right_text_entry : public simple_text_entry {
221114402Srupublic:
222114402Sru  right_text_entry(char *s, const entry_modifier *m);
223114402Sru  void simple_print(int);
224114402Sru  void add_tab();
225114402Sru};
226114402Sru
227114402Sruclass center_text_entry : public simple_text_entry {
228114402Srupublic:
229114402Sru  center_text_entry(char *s, const entry_modifier *m);
230114402Sru  void simple_print(int);
231114402Sru  void add_tab();
232114402Sru};
233114402Sru
234114402Sruclass numeric_text_entry : public text_entry {
235114402Sru  int dot_pos;
236114402Srupublic:
237114402Sru  numeric_text_entry(char *s, const entry_modifier *m, int pos);
238114402Sru  void do_width();
239114402Sru  void simple_print(int);
240114402Sru};
241114402Sru
242114402Sruclass alphabetic_text_entry : public text_entry {
243114402Srupublic:
244114402Sru  alphabetic_text_entry(char *s, const entry_modifier *m);
245114402Sru  void do_width();
246114402Sru  void simple_print(int);
247114402Sru  void add_tab();
248114402Sru};
249114402Sru
250114402Sruclass line_entry : public simple_entry {
251114402Sruprotected:
252114402Sru  char double_vrule_on_right;
253114402Sru  char double_vrule_on_left;
254114402Srupublic:
255114402Sru  line_entry(const entry_modifier *);
256114402Sru  void note_double_vrule_on_right(int);
257114402Sru  void note_double_vrule_on_left(int);
258114402Sru  void simple_print(int) = 0;
259114402Sru};
260114402Sru
261114402Sruclass single_line_entry : public line_entry {
262114402Srupublic:
263114402Sru  single_line_entry(const entry_modifier *m);
264114402Sru  void simple_print(int);
265114402Sru  single_line_entry *to_single_line_entry();
266114402Sru  int line_type();
267114402Sru};
268114402Sru
269114402Sruclass double_line_entry : public line_entry {
270114402Srupublic:
271114402Sru  double_line_entry(const entry_modifier *m);
272114402Sru  void simple_print(int);
273114402Sru  double_line_entry *to_double_line_entry();
274114402Sru  int line_type();
275114402Sru};
276114402Sru
277114402Sruclass short_line_entry : public simple_entry {
278114402Srupublic:
279114402Sru  short_line_entry(const entry_modifier *m);
280114402Sru  void simple_print(int);
281114402Sru  int line_type();
282114402Sru};
283114402Sru
284114402Sruclass short_double_line_entry : public simple_entry {
285114402Srupublic:
286114402Sru  short_double_line_entry(const entry_modifier *m);
287114402Sru  void simple_print(int);
288114402Sru  int line_type();
289114402Sru};
290114402Sru
291114402Sruclass block_entry : public table_entry {
292114402Sru  char *contents;
293114402Sruprotected:
294114402Sru  void do_divert(int alphabetic, int ncols, const string *mw, int *sep);
295114402Srupublic:
296114402Sru  block_entry(char *s, const entry_modifier *m);
297114402Sru  ~block_entry();
298114402Sru  int divert(int ncols, const string *mw, int *sep);
299114402Sru  void do_width();
300114402Sru  void do_depth();
301114402Sru  void position_vertically();
302114402Sru  void print() = 0;
303114402Sru};
304114402Sru
305114402Sruclass left_block_entry : public block_entry {
306114402Srupublic:
307114402Sru  left_block_entry(char *s, const entry_modifier *m);
308114402Sru  void print();
309114402Sru};
310114402Sru
311114402Sruclass right_block_entry : public block_entry {
312114402Srupublic:
313114402Sru  right_block_entry(char *s, const entry_modifier *m);
314114402Sru  void print();
315114402Sru};
316114402Sru
317114402Sruclass center_block_entry : public block_entry {
318114402Srupublic:
319114402Sru  center_block_entry(char *s, const entry_modifier *m);
320114402Sru  void print();
321114402Sru};
322114402Sru
323114402Sruclass alphabetic_block_entry : public block_entry {
324114402Srupublic:
325114402Sru  alphabetic_block_entry(char *s, const entry_modifier *m);
326114402Sru  void print();
327114402Sru  int divert(int ncols, const string *mw, int *sep);
328114402Sru};
329114402Sru
330114402Srutable_entry::table_entry(const entry_modifier *m)
331114402Sru: next(0), input_lineno(-1), input_filename(0),
332114402Sru  start_row(-1), end_row(-1), start_col(-1), end_col(-1), mod(m)
333114402Sru{
334114402Sru}
335114402Sru
336114402Srutable_entry::~table_entry()
337114402Sru{
338114402Sru}
339114402Sru
340114402Sruint table_entry::divert(int, const string *, int *)
341114402Sru{
342114402Sru  return 0;
343114402Sru}
344114402Sru
345114402Sruvoid table_entry::do_width()
346114402Sru{
347114402Sru}
348114402Sru
349114402Srusingle_line_entry *table_entry::to_single_line_entry()
350114402Sru{
351114402Sru  return 0;
352114402Sru}
353114402Sru
354114402Srudouble_line_entry *table_entry::to_double_line_entry()
355114402Sru{
356114402Sru  return 0;
357114402Sru}
358114402Sru
359114402Srusimple_entry *table_entry::to_simple_entry()
360114402Sru{
361114402Sru  return 0;
362114402Sru}
363114402Sru
364114402Sruvoid table_entry::do_depth()
365114402Sru{
366114402Sru}
367114402Sru
368114402Sruvoid table_entry::set_location()
369114402Sru{
370114402Sru  set_troff_location(input_filename, input_lineno);
371114402Sru}
372114402Sru
373114402Sruint table_entry::line_type()
374114402Sru{
375114402Sru  return -1;
376114402Sru}
377114402Sru
378114402Sruvoid table_entry::note_double_vrule_on_right(int)
379114402Sru{
380114402Sru}
381114402Sru
382114402Sruvoid table_entry::note_double_vrule_on_left(int)
383114402Sru{
384114402Sru}
385114402Sru
386114402Srusimple_entry::simple_entry(const entry_modifier *m) : table_entry(m)
387114402Sru{
388114402Sru}
389114402Sru
390114402Sruvoid simple_entry::add_tab()
391114402Sru{
392114402Sru  // do nothing
393114402Sru}
394114402Sru
395114402Sruvoid simple_entry::simple_print(int)
396114402Sru{
397114402Sru  // do nothing
398114402Sru}
399114402Sru
400114402Sruvoid simple_entry::position_vertically()
401114402Sru{
402114402Sru  if (start_row != end_row)
403114402Sru    switch (mod->vertical_alignment) {
404114402Sru    case entry_modifier::TOP:
405114402Sru      printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
406114402Sru      break;
407114402Sru    case entry_modifier::CENTER:
408114402Sru      // Peform the motion in two stages so that the center is rounded
409114402Sru      // vertically upwards even if net vertical motion is upwards.
410114402Sru      printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
411114402Sru      printfs(".sp \\n[" BOTTOM_REG "]u-\\n[%1]u-1v/2u\n",
412114402Sru	      row_start_reg(start_row));
413114402Sru      break;
414114402Sru    case entry_modifier::BOTTOM:
415114402Sru      printfs(".sp |\\n[%1]u+\\n[" BOTTOM_REG "]u-\\n[%1]u-1v\n",
416114402Sru	      row_start_reg(start_row));
417114402Sru      break;
418114402Sru    default:
419114402Sru      assert(0);
420114402Sru    }
421114402Sru}
422114402Sru
423114402Sruvoid simple_entry::print()
424114402Sru{
425114402Sru  prints(".ta");
426114402Sru  add_tab();
427114402Sru  prints('\n');
428114402Sru  set_location();
429114402Sru  prints("\\&");
430114402Sru  simple_print(0);
431114402Sru  prints('\n');
432114402Sru}
433114402Sru
434114402Srusimple_entry *simple_entry::to_simple_entry()
435114402Sru{
436114402Sru  return this;
437114402Sru}
438114402Sru
439114402Sruempty_entry::empty_entry(const entry_modifier *m)
440114402Sru: simple_entry(m)
441114402Sru{
442114402Sru}
443114402Sru
444114402Sruint empty_entry::line_type()
445114402Sru{
446114402Sru  return 0;
447114402Sru}
448114402Sru
449114402Srutext_entry::text_entry(char *s, const entry_modifier *m)
450114402Sru: simple_entry(m), contents(s)
451114402Sru{
452114402Sru}
453114402Sru
454114402Srutext_entry::~text_entry()
455114402Sru{
456114402Sru  a_delete contents;
457114402Sru}
458114402Sru
459114402Srurepeated_char_entry::repeated_char_entry(char *s, const entry_modifier *m)
460114402Sru: text_entry(s, m)
461114402Sru{
462114402Sru}
463114402Sru
464114402Sruvoid repeated_char_entry::simple_print(int)
465114402Sru{
466114402Sru  printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
467114402Sru  set_inline_modifier(mod);
468114402Sru  printfs("\\l" DELIMITER_CHAR "\\n[%1]u\\&",
469114402Sru	  span_width_reg(start_col, end_col));
470114402Sru  prints(contents);
471114402Sru  prints(DELIMITER_CHAR);
472114402Sru  restore_inline_modifier(mod);
473114402Sru}
474114402Sru
475114402Srusimple_text_entry::simple_text_entry(char *s, const entry_modifier *m)
476114402Sru: text_entry(s, m)
477114402Sru{
478114402Sru}
479114402Sru
480114402Sruvoid simple_text_entry::do_width()
481114402Sru{
482114402Sru  set_location();
483114402Sru  printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR,
484114402Sru	  span_width_reg(start_col, end_col));
485114402Sru  print_contents();
486114402Sru  prints(DELIMITER_CHAR "\n");
487114402Sru}
488114402Sru
489114402Sruleft_text_entry::left_text_entry(char *s, const entry_modifier *m)
490114402Sru: simple_text_entry(s, m)
491114402Sru{
492114402Sru}
493114402Sru
494114402Sruvoid left_text_entry::simple_print(int)
495114402Sru{
496114402Sru  printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
497114402Sru  print_contents();
498114402Sru}
499114402Sru
500114402Sru// The only point of this is to make `\a' ``work'' as in Unix tbl.  Grrr.
501114402Sru
502114402Sruvoid left_text_entry::add_tab()
503114402Sru{
504114402Sru  printfs(" \\n[%1]u", column_end_reg(end_col));
505114402Sru}
506114402Sru
507114402Sruright_text_entry::right_text_entry(char *s, const entry_modifier *m)
508114402Sru: simple_text_entry(s, m)
509114402Sru{
510114402Sru}
511114402Sru
512114402Sruvoid right_text_entry::simple_print(int)
513114402Sru{
514114402Sru  printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
515114402Sru  prints("\002\003");
516114402Sru  print_contents();
517114402Sru  prints("\002");
518114402Sru}
519114402Sru
520114402Sruvoid right_text_entry::add_tab()
521114402Sru{
522114402Sru  printfs(" \\n[%1]u", column_end_reg(end_col));
523114402Sru}
524114402Sru
525114402Srucenter_text_entry::center_text_entry(char *s, const entry_modifier *m)
526114402Sru: simple_text_entry(s, m)
527114402Sru{
528114402Sru}
529114402Sru
530114402Sruvoid center_text_entry::simple_print(int)
531114402Sru{
532114402Sru  printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
533114402Sru  prints("\002\003");
534114402Sru  print_contents();
535114402Sru  prints("\003\002");
536114402Sru}
537114402Sru
538114402Sruvoid center_text_entry::add_tab()
539114402Sru{
540114402Sru  printfs(" \\n[%1]u", column_end_reg(end_col));
541114402Sru}
542114402Sru
543114402Srunumeric_text_entry::numeric_text_entry(char *s, const entry_modifier *m, int pos)
544114402Sru: text_entry(s, m), dot_pos(pos)
545114402Sru{
546114402Sru}
547114402Sru
548114402Sruvoid numeric_text_entry::do_width()
549114402Sru{
550114402Sru  if (dot_pos != 0) {
551114402Sru    set_location();
552114402Sru    printfs(".nr %1 0\\w" DELIMITER_CHAR,
553114402Sru	    block_width_reg(start_row, start_col));
554114402Sru    set_inline_modifier(mod);
555114402Sru    for (int i = 0; i < dot_pos; i++)
556114402Sru      prints(contents[i]);
557114402Sru    restore_inline_modifier(mod);
558114402Sru    prints(DELIMITER_CHAR "\n");
559114402Sru    printfs(".nr %1 \\n[%1]>?\\n[%2]\n",
560114402Sru	    span_left_numeric_width_reg(start_col, end_col),
561114402Sru	    block_width_reg(start_row, start_col));
562114402Sru  }
563114402Sru  else
564114402Sru    printfs(".nr %1 0\n", block_width_reg(start_row, start_col));
565114402Sru  if (contents[dot_pos] != '\0') {
566114402Sru    set_location();
567114402Sru    printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR,
568114402Sru	    span_right_numeric_width_reg(start_col, end_col));
569114402Sru    set_inline_modifier(mod);
570114402Sru    prints(contents + dot_pos);
571114402Sru    restore_inline_modifier(mod);
572114402Sru    prints(DELIMITER_CHAR "\n");
573114402Sru  }
574114402Sru}
575114402Sru
576114402Sruvoid numeric_text_entry::simple_print(int)
577114402Sru{
578114402Sru  printfs("\\h'|(\\n[%1]u-\\n[%2]u-\\n[%3]u/2u+\\n[%2]u+\\n[%4]u-\\n[%5]u)'",
579114402Sru	  span_width_reg(start_col, end_col),
580114402Sru	  span_left_numeric_width_reg(start_col, end_col),
581114402Sru	  span_right_numeric_width_reg(start_col, end_col),
582114402Sru	  column_start_reg(start_col),
583114402Sru	  block_width_reg(start_row, start_col));
584114402Sru  print_contents();
585114402Sru}
586114402Sru
587114402Srualphabetic_text_entry::alphabetic_text_entry(char *s, const entry_modifier *m)
588114402Sru: text_entry(s, m)
589114402Sru{
590114402Sru}
591114402Sru
592114402Sruvoid alphabetic_text_entry::do_width()
593114402Sru{
594114402Sru  set_location();
595114402Sru  printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR,
596114402Sru	  span_alphabetic_width_reg(start_col, end_col));
597114402Sru  print_contents();
598114402Sru  prints(DELIMITER_CHAR "\n");
599114402Sru}
600114402Sru
601114402Sruvoid alphabetic_text_entry::simple_print(int)
602114402Sru{
603114402Sru  printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
604114402Sru  printfs("\\h'\\n[%1]u-\\n[%2]u/2u'",
605114402Sru	  span_width_reg(start_col, end_col),
606114402Sru	  span_alphabetic_width_reg(start_col, end_col));
607114402Sru  print_contents();
608114402Sru}
609114402Sru
610114402Sru// The only point of this is to make `\a' ``work'' as in Unix tbl.  Grrr.
611114402Sru
612114402Sruvoid alphabetic_text_entry::add_tab()
613114402Sru{
614114402Sru  printfs(" \\n[%1]u", column_end_reg(end_col));
615114402Sru}
616114402Sru
617114402Srublock_entry::block_entry(char *s, const entry_modifier *m)
618114402Sru: table_entry(m), contents(s)
619114402Sru{
620114402Sru}
621114402Sru
622114402Srublock_entry::~block_entry()
623114402Sru{
624114402Sru  a_delete contents;
625114402Sru}
626114402Sru
627114402Sruvoid block_entry::position_vertically()
628114402Sru{
629114402Sru  if (start_row != end_row)
630114402Sru    switch(mod->vertical_alignment) {
631114402Sru    case entry_modifier::TOP:
632114402Sru      printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
633114402Sru      break;
634114402Sru    case entry_modifier::CENTER:
635114402Sru      // Peform the motion in two stages so that the center is rounded
636114402Sru      // vertically upwards even if net vertical motion is upwards.
637114402Sru      printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
638114402Sru      printfs(".sp \\n[" BOTTOM_REG "]u-\\n[%1]u-\\n[%2]u/2u\n",
639114402Sru	      row_start_reg(start_row),
640114402Sru	      block_height_reg(start_row, start_col));
641114402Sru      break;
642114402Sru    case entry_modifier::BOTTOM:
643114402Sru      printfs(".sp |\\n[%1]u+\\n[" BOTTOM_REG "]u-\\n[%1]u-\\n[%2]u\n",
644114402Sru	      row_start_reg(start_row),
645114402Sru	      block_height_reg(start_row, start_col));
646114402Sru      break;
647114402Sru    default:
648114402Sru      assert(0);
649114402Sru    }
650114402Sru  if (mod->stagger)
651114402Sru    prints(".sp -.5v\n");
652114402Sru}
653114402Sru
654114402Sruint block_entry::divert(int ncols, const string *mw, int *sep)
655114402Sru{
656114402Sru  do_divert(0, ncols, mw, sep);
657114402Sru  return 1;
658114402Sru}
659114402Sru
660114402Sruvoid block_entry::do_divert(int alphabetic, int ncols, const string *mw,
661114402Sru			    int *sep)
662114402Sru{
663114402Sru  printfs(".di %1\n", block_diversion_name(start_row, start_col));
664114402Sru  prints(".if \\n[" SAVED_FILL_REG "] .fi\n"
665114402Sru	 ".in 0\n");
666114402Sru  prints(".ll ");
667114402Sru  int i;
668114402Sru  for (i = start_col; i <= end_col; i++)
669114402Sru    if (mw[i].empty())
670114402Sru      break;
671114402Sru  if (i > end_col) {
672114402Sru    // Every column spanned by this entry has a minimum width.
673114402Sru    for (int j = start_col; j <= end_col; j++) {
674114402Sru      if (j > start_col) {
675114402Sru	if (sep)
676114402Sru	  printfs("+%1n", as_string(sep[j - 1]));
677114402Sru	prints('+');
678114402Sru      }
679114402Sru      printfs("(n;%1)", mw[j]);
680114402Sru    }
681114402Sru    printfs(">?\\n[%1]u", span_width_reg(start_col, end_col));
682114402Sru  }
683114402Sru  else
684114402Sru    printfs("(u;\\n[%1]>?(\\n[.l]*%2/%3))",
685114402Sru	    span_width_reg(start_col, end_col),
686114402Sru	    as_string(end_col - start_col + 1),
687114402Sru	    as_string(ncols + 1));
688114402Sru  if (alphabetic)
689114402Sru    prints("-2n");
690114402Sru  prints("\n");
691151497Sru  prints(".cp \\n(" COMPATIBLE_REG "\n");
692114402Sru  set_modifier(mod);
693114402Sru  set_location();
694114402Sru  prints(contents);
695114402Sru  prints(".br\n.di\n.cp 0\n");
696114402Sru  if (!mod->zero_width) {
697114402Sru    if (alphabetic) {
698114402Sru      printfs(".nr %1 \\n[%1]>?(\\n[dl]+2n)\n",
699114402Sru	      span_width_reg(start_col, end_col));
700114402Sru      printfs(".nr %1 \\n[%1]>?\\n[dl]\n",
701114402Sru	      span_alphabetic_width_reg(start_col, end_col));
702114402Sru    }
703114402Sru    else
704114402Sru      printfs(".nr %1 \\n[%1]>?\\n[dl]\n", span_width_reg(start_col, end_col));
705114402Sru  }
706114402Sru  printfs(".nr %1 \\n[dn]\n", block_height_reg(start_row, start_col));
707114402Sru  printfs(".nr %1 \\n[dl]\n", block_width_reg(start_row, start_col));
708114402Sru  prints("." RESET_MACRO_NAME "\n"
709114402Sru	 ".in \\n[" SAVED_INDENT_REG "]u\n"
710114402Sru	 ".nf\n");
711114402Sru  // the block might have contained .lf commands
712114402Sru  location_force_filename = 1;
713114402Sru}
714114402Sru
715114402Sruvoid block_entry::do_width()
716114402Sru{
717114402Sru  // do nothing; the action happens in divert
718114402Sru}
719114402Sru
720114402Sruvoid block_entry::do_depth()
721114402Sru{
722114402Sru  printfs(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?(\\n[%1]+\\n[%2])\n",
723114402Sru	  row_start_reg(start_row),
724114402Sru	  block_height_reg(start_row, start_col));
725114402Sru}
726114402Sru
727114402Sruleft_block_entry::left_block_entry(char *s, const entry_modifier *m)
728114402Sru: block_entry(s, m)
729114402Sru{
730114402Sru}
731114402Sru
732114402Sruvoid left_block_entry::print()
733114402Sru{
734114402Sru  printfs(".in +\\n[%1]u\n", column_start_reg(start_col));
735114402Sru  printfs(".%1\n", block_diversion_name(start_row, start_col));
736114402Sru  prints(".in\n");
737114402Sru}
738114402Sru
739114402Sruright_block_entry::right_block_entry(char *s, const entry_modifier *m)
740114402Sru: block_entry(s, m)
741114402Sru{
742114402Sru}
743114402Sru
744114402Sruvoid right_block_entry::print()
745114402Sru{
746114402Sru  printfs(".in +\\n[%1]u+\\n[%2]u-\\n[%3]u\n",
747114402Sru	  column_start_reg(start_col),
748114402Sru	  span_width_reg(start_col, end_col),
749114402Sru	  block_width_reg(start_row, start_col));
750114402Sru  printfs(".%1\n", block_diversion_name(start_row, start_col));
751114402Sru  prints(".in\n");
752114402Sru}
753114402Sru
754114402Srucenter_block_entry::center_block_entry(char *s, const entry_modifier *m)
755114402Sru: block_entry(s, m)
756114402Sru{
757114402Sru}
758114402Sru
759114402Sruvoid center_block_entry::print()
760114402Sru{
761114402Sru  printfs(".in +\\n[%1]u+(\\n[%2]u-\\n[%3]u/2u)\n",
762114402Sru	  column_start_reg(start_col),
763114402Sru	  span_width_reg(start_col, end_col),
764114402Sru	  block_width_reg(start_row, start_col));
765114402Sru  printfs(".%1\n", block_diversion_name(start_row, start_col));
766114402Sru  prints(".in\n");
767114402Sru}
768114402Sru
769114402Srualphabetic_block_entry::alphabetic_block_entry(char *s,
770114402Sru					       const entry_modifier *m)
771114402Sru: block_entry(s, m)
772114402Sru{
773114402Sru}
774114402Sru
775114402Sruint alphabetic_block_entry::divert(int ncols, const string *mw, int *sep)
776114402Sru{
777114402Sru  do_divert(1, ncols, mw, sep);
778114402Sru  return 1;
779114402Sru}
780114402Sru
781114402Sruvoid alphabetic_block_entry::print()
782114402Sru{
783114402Sru  printfs(".in +\\n[%1]u+(\\n[%2]u-\\n[%3]u/2u)\n",
784114402Sru	  column_start_reg(start_col),
785114402Sru	  span_width_reg(start_col, end_col),
786114402Sru	  span_alphabetic_width_reg(start_col, end_col));
787114402Sru  printfs(".%1\n", block_diversion_name(start_row, start_col));
788114402Sru  prints(".in\n");
789114402Sru}
790114402Sru
791114402Sruline_entry::line_entry(const entry_modifier *m)
792114402Sru: simple_entry(m), double_vrule_on_right(0), double_vrule_on_left(0)
793114402Sru{
794114402Sru}
795114402Sru
796114402Sruvoid line_entry::note_double_vrule_on_right(int is_corner)
797114402Sru{
798114402Sru  double_vrule_on_right = is_corner ? 1 : 2;
799114402Sru}
800114402Sru
801114402Sruvoid line_entry::note_double_vrule_on_left(int is_corner)
802114402Sru{
803114402Sru  double_vrule_on_left = is_corner ? 1 : 2;
804114402Sru}
805114402Sru
806114402Srusingle_line_entry::single_line_entry(const entry_modifier *m)
807114402Sru: line_entry(m)
808114402Sru{
809114402Sru}
810114402Sru
811114402Sruint single_line_entry::line_type()
812114402Sru{
813114402Sru  return 1;
814114402Sru}
815114402Sru
816114402Sruvoid single_line_entry::simple_print(int dont_move)
817114402Sru{
818114402Sru  printfs("\\h'|\\n[%1]u",
819114402Sru	  column_divide_reg(start_col));
820114402Sru  if (double_vrule_on_left) {
821114402Sru    prints(double_vrule_on_left == 1 ? "-" : "+");
822114402Sru    prints(HALF_DOUBLE_LINE_SEP);
823114402Sru  }
824114402Sru  prints("'");
825114402Sru  if (!dont_move)
826114402Sru    prints("\\v'-" BAR_HEIGHT "'");
827114402Sru  printfs("\\s[\\n[" LINESIZE_REG "]]" "\\D'l |\\n[%1]u",
828114402Sru	  column_divide_reg(end_col+1));
829114402Sru  if (double_vrule_on_right) {
830114402Sru    prints(double_vrule_on_left == 1 ? "+" : "-");
831114402Sru    prints(HALF_DOUBLE_LINE_SEP);
832114402Sru  }
833114402Sru  prints("0'\\s0");
834114402Sru  if (!dont_move)
835114402Sru    prints("\\v'" BAR_HEIGHT "'");
836114402Sru}
837114402Sru
838114402Srusingle_line_entry *single_line_entry::to_single_line_entry()
839114402Sru{
840114402Sru  return this;
841114402Sru}
842114402Sru
843114402Srudouble_line_entry::double_line_entry(const entry_modifier *m)
844114402Sru: line_entry(m)
845114402Sru{
846114402Sru}
847114402Sru
848114402Sruint double_line_entry::line_type()
849114402Sru{
850114402Sru  return 2;
851114402Sru}
852114402Sru
853114402Sruvoid double_line_entry::simple_print(int dont_move)
854114402Sru{
855114402Sru  if (!dont_move)
856114402Sru    prints("\\v'-" BAR_HEIGHT "'");
857114402Sru  printfs("\\h'|\\n[%1]u",
858114402Sru	  column_divide_reg(start_col));
859114402Sru  if (double_vrule_on_left) {
860114402Sru    prints(double_vrule_on_left == 1 ? "-" : "+");
861114402Sru    prints(HALF_DOUBLE_LINE_SEP);
862114402Sru  }
863114402Sru  prints("'");
864114402Sru  printfs("\\v'-" HALF_DOUBLE_LINE_SEP "'"
865114402Sru	  "\\s[\\n[" LINESIZE_REG "]]"
866114402Sru	  "\\D'l |\\n[%1]u",
867114402Sru	  column_divide_reg(end_col+1));
868114402Sru  if (double_vrule_on_right)
869114402Sru    prints("-" HALF_DOUBLE_LINE_SEP);
870114402Sru  prints(" 0'");
871114402Sru  printfs("\\v'" DOUBLE_LINE_SEP "'"
872114402Sru	  "\\D'l |\\n[%1]u",
873114402Sru	  column_divide_reg(start_col));
874114402Sru  if (double_vrule_on_right) {
875114402Sru    prints(double_vrule_on_left == 1 ? "+" : "-");
876114402Sru    prints(HALF_DOUBLE_LINE_SEP);
877114402Sru  }
878114402Sru  prints(" 0'");
879114402Sru  prints("\\s0"
880114402Sru	 "\\v'-" HALF_DOUBLE_LINE_SEP "'");
881114402Sru  if (!dont_move)
882114402Sru    prints("\\v'" BAR_HEIGHT "'");
883114402Sru}
884114402Sru
885114402Srudouble_line_entry *double_line_entry::to_double_line_entry()
886114402Sru{
887114402Sru  return this;
888114402Sru}
889114402Sru
890114402Srushort_line_entry::short_line_entry(const entry_modifier *m)
891114402Sru: simple_entry(m)
892114402Sru{
893114402Sru}
894114402Sru
895114402Sruint short_line_entry::line_type()
896114402Sru{
897114402Sru  return 1;
898114402Sru}
899114402Sru
900114402Sruvoid short_line_entry::simple_print(int dont_move)
901114402Sru{
902114402Sru  if (mod->stagger)
903114402Sru    prints("\\v'-.5v'");
904114402Sru  if (!dont_move)
905114402Sru    prints("\\v'-" BAR_HEIGHT "'");
906114402Sru  printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
907114402Sru  printfs("\\s[\\n[" LINESIZE_REG "]]"
908114402Sru	  "\\D'l \\n[%1]u 0'"
909114402Sru	  "\\s0",
910114402Sru	  span_width_reg(start_col, end_col));
911114402Sru  if (!dont_move)
912114402Sru    prints("\\v'" BAR_HEIGHT "'");
913114402Sru  if (mod->stagger)
914114402Sru    prints("\\v'.5v'");
915114402Sru}
916114402Sru
917114402Srushort_double_line_entry::short_double_line_entry(const entry_modifier *m)
918114402Sru: simple_entry(m)
919114402Sru{
920114402Sru}
921114402Sru
922114402Sruint short_double_line_entry::line_type()
923114402Sru{
924114402Sru  return 2;
925114402Sru}
926114402Sru
927114402Sruvoid short_double_line_entry::simple_print(int dont_move)
928114402Sru{
929114402Sru  if (mod->stagger)
930114402Sru    prints("\\v'-.5v'");
931114402Sru  if (!dont_move)
932114402Sru    prints("\\v'-" BAR_HEIGHT "'");
933114402Sru  printfs("\\h'|\\n[%2]u'"
934114402Sru	  "\\v'-" HALF_DOUBLE_LINE_SEP "'"
935114402Sru	  "\\s[\\n[" LINESIZE_REG "]]"
936114402Sru	  "\\D'l \\n[%1]u 0'"
937114402Sru	  "\\v'" DOUBLE_LINE_SEP "'"
938114402Sru	  "\\D'l |\\n[%2]u 0'"
939114402Sru	  "\\s0"
940114402Sru	  "\\v'-" HALF_DOUBLE_LINE_SEP "'",
941114402Sru	  span_width_reg(start_col, end_col),
942114402Sru	  column_start_reg(start_col));
943114402Sru  if (!dont_move)
944114402Sru    prints("\\v'" BAR_HEIGHT "'");
945114402Sru  if (mod->stagger)
946114402Sru    prints("\\v'.5v'");
947114402Sru}
948114402Sru
949114402Sruvoid set_modifier(const entry_modifier *m)
950114402Sru{
951114402Sru  if (!m->font.empty())
952114402Sru    printfs(".ft %1\n", m->font);
953114402Sru  if (m->point_size.val != 0) {
954114402Sru    prints(".ps ");
955114402Sru    if (m->point_size.inc > 0)
956114402Sru      prints('+');
957114402Sru    else if (m->point_size.inc < 0)
958114402Sru      prints('-');
959114402Sru    printfs("%1\n", as_string(m->point_size.val));
960114402Sru  }
961114402Sru  if (m->vertical_spacing.val != 0) {
962114402Sru    prints(".vs ");
963114402Sru    if (m->vertical_spacing.inc > 0)
964114402Sru      prints('+');
965114402Sru    else if (m->vertical_spacing.inc < 0)
966114402Sru      prints('-');
967114402Sru    printfs("%1\n", as_string(m->vertical_spacing.val));
968114402Sru  }
969151497Sru  if (!m->macro.empty())
970151497Sru    printfs(".%1\n", m->macro);
971114402Sru}
972114402Sru
973114402Sruvoid set_inline_modifier(const entry_modifier *m)
974114402Sru{
975114402Sru  if (!m->font.empty())
976114402Sru    printfs("\\f[%1]", m->font);
977114402Sru  if (m->point_size.val != 0) {
978114402Sru    prints("\\s[");
979114402Sru    if (m->point_size.inc > 0)
980114402Sru      prints('+');
981114402Sru    else if (m->point_size.inc < 0)
982114402Sru      prints('-');
983114402Sru    printfs("%1]", as_string(m->point_size.val));
984114402Sru  }
985114402Sru  if (m->stagger)
986114402Sru    prints("\\v'-.5v'");
987114402Sru}
988114402Sru
989114402Sruvoid restore_inline_modifier(const entry_modifier *m)
990114402Sru{
991114402Sru  if (!m->font.empty())
992114402Sru    prints("\\f[\\n[" SAVED_FONT_REG "]]");
993114402Sru  if (m->point_size.val != 0)
994114402Sru    prints("\\s[\\n[" SAVED_SIZE_REG "]]");
995114402Sru  if (m->stagger)
996114402Sru    prints("\\v'.5v'");
997114402Sru}
998114402Sru
999114402Srustruct stuff {
1000114402Sru  stuff *next;
1001114402Sru  int row;			// occurs before row `row'
1002114402Sru  char printed;			// has it been printed?
1003114402Sru
1004114402Sru  stuff(int);
1005114402Sru  virtual void print(table *) = 0;
1006114402Sru  virtual ~stuff();
1007114402Sru  virtual int is_single_line() { return 0; };
1008114402Sru  virtual int is_double_line() { return 0; };
1009114402Sru};
1010114402Sru
1011114402Srustuff::stuff(int r) : next(0), row(r), printed(0)
1012114402Sru{
1013114402Sru}
1014114402Sru
1015114402Srustuff::~stuff()
1016114402Sru{
1017114402Sru}
1018114402Sru
1019114402Srustruct text_stuff : public stuff {
1020114402Sru  string contents;
1021114402Sru  const char *filename;
1022114402Sru  int lineno;
1023114402Sru
1024114402Sru  text_stuff(const string &, int r, const char *fn, int ln);
1025114402Sru  ~text_stuff();
1026114402Sru  void print(table *);
1027114402Sru};
1028114402Sru
1029114402Srutext_stuff::text_stuff(const string &s, int r, const char *fn, int ln)
1030114402Sru: stuff(r), contents(s), filename(fn), lineno(ln)
1031114402Sru{
1032114402Sru}
1033114402Sru
1034114402Srutext_stuff::~text_stuff()
1035114402Sru{
1036114402Sru}
1037114402Sru
1038114402Sruvoid text_stuff::print(table *)
1039114402Sru{
1040114402Sru  printed = 1;
1041114402Sru  prints(".cp \\n(" COMPATIBLE_REG "\n");
1042114402Sru  set_troff_location(filename, lineno);
1043114402Sru  prints(contents);
1044114402Sru  prints(".cp 0\n");
1045114402Sru  location_force_filename = 1;	// it might have been a .lf command
1046114402Sru}
1047114402Sru
1048114402Srustruct single_hline_stuff : public stuff {
1049114402Sru  single_hline_stuff(int r);
1050114402Sru  void print(table *);
1051114402Sru  int is_single_line();
1052114402Sru};
1053114402Sru
1054114402Srusingle_hline_stuff::single_hline_stuff(int r) : stuff(r)
1055114402Sru{
1056114402Sru}
1057114402Sru
1058114402Sruvoid single_hline_stuff::print(table *tbl)
1059114402Sru{
1060114402Sru  printed = 1;
1061114402Sru  tbl->print_single_hline(row);
1062114402Sru}
1063114402Sru
1064114402Sruint single_hline_stuff::is_single_line()
1065114402Sru{
1066114402Sru  return 1;
1067114402Sru}
1068114402Sru
1069114402Srustruct double_hline_stuff : stuff {
1070114402Sru  double_hline_stuff(int r);
1071114402Sru  void print(table *);
1072114402Sru  int is_double_line();
1073114402Sru};
1074114402Sru
1075114402Srudouble_hline_stuff::double_hline_stuff(int r) : stuff(r)
1076114402Sru{
1077114402Sru}
1078114402Sru
1079114402Sruvoid double_hline_stuff::print(table *tbl)
1080114402Sru{
1081114402Sru  printed = 1;
1082114402Sru  tbl->print_double_hline(row);
1083114402Sru}
1084114402Sru
1085114402Sruint double_hline_stuff::is_double_line()
1086114402Sru{
1087114402Sru  return 1;
1088114402Sru}
1089114402Sru
1090114402Srustruct vertical_rule {
1091114402Sru  vertical_rule *next;
1092114402Sru  int start_row;
1093114402Sru  int end_row;
1094151497Sru  int col;
1095114402Sru  char is_double;
1096114402Sru  string top_adjust;
1097114402Sru  string bot_adjust;
1098114402Sru
1099114402Sru  vertical_rule(int sr, int er, int c, int dbl, vertical_rule *);
1100114402Sru  ~vertical_rule();
1101114402Sru  void contribute_to_bottom_macro(table *);
1102114402Sru  void print();
1103114402Sru};
1104114402Sru
1105114402Sruvertical_rule::vertical_rule(int sr, int er, int c, int dbl, vertical_rule *p)
1106114402Sru: next(p), start_row(sr), end_row(er), col(c), is_double(dbl)
1107114402Sru{
1108114402Sru}
1109114402Sru
1110114402Sruvertical_rule::~vertical_rule()
1111114402Sru{
1112114402Sru}
1113114402Sru
1114114402Sruvoid vertical_rule::contribute_to_bottom_macro(table *tbl)
1115114402Sru{
1116114402Sru  printfs(".if \\n[" CURRENT_ROW_REG "]>=%1",
1117114402Sru	  as_string(start_row));
1118114402Sru  if (end_row != tbl->get_nrows() - 1)
1119114402Sru    printfs("&(\\n[" CURRENT_ROW_REG "]<%1)",
1120114402Sru	    as_string(end_row));
1121114402Sru  prints(" \\{");
1122114402Sru  printfs(".if %1<=\\n[" LAST_PASSED_ROW_REG "] .nr %2 \\n[#T]\n",
1123114402Sru	  as_string(start_row),
1124114402Sru	  row_top_reg(start_row));
1125114402Sru  const char *offset_table[3];
1126114402Sru  if (is_double) {
1127114402Sru    offset_table[0] = "-" HALF_DOUBLE_LINE_SEP;
1128114402Sru    offset_table[1] = "+" HALF_DOUBLE_LINE_SEP;
1129114402Sru    offset_table[2] = 0;
1130114402Sru  }
1131114402Sru  else {
1132114402Sru    offset_table[0] = "";
1133114402Sru    offset_table[1] = 0;
1134114402Sru  }
1135114402Sru  for (const char **offsetp = offset_table; *offsetp; offsetp++) {
1136114402Sru    prints(".sp -1\n"
1137114402Sru	   "\\v'" BODY_DEPTH);
1138114402Sru    if (!bot_adjust.empty())
1139114402Sru      printfs("+%1", bot_adjust);
1140114402Sru    prints("'");
1141114402Sru    printfs("\\h'\\n[%1]u%3'\\s[\\n[" LINESIZE_REG "]]\\D'l 0 |\\n[%2]u-1v",
1142114402Sru	    column_divide_reg(col),
1143114402Sru	    row_top_reg(start_row),
1144114402Sru	    *offsetp);
1145114402Sru    if (!bot_adjust.empty())
1146114402Sru      printfs("-(%1)", bot_adjust);
1147114402Sru    // don't perform the top adjustment if the top is actually #T
1148114402Sru    if (!top_adjust.empty())
1149114402Sru      printfs("+((%1)*(%2>\\n[" LAST_PASSED_ROW_REG "]))",
1150114402Sru	      top_adjust,
1151114402Sru	      as_string(start_row));
1152114402Sru    prints("'\\s0\n");
1153114402Sru  }
1154114402Sru  prints(".\\}\n");
1155114402Sru}
1156114402Sru
1157114402Sruvoid vertical_rule::print()
1158114402Sru{
1159114402Sru  printfs("\\*[" TRANSPARENT_STRING_NAME "]"
1160114402Sru	  ".if %1<=\\*[" QUOTE_STRING_NAME "]\\n[" LAST_PASSED_ROW_REG "] "
1161114402Sru	  ".nr %2 \\*[" QUOTE_STRING_NAME "]\\n[#T]\n",
1162114402Sru	  as_string(start_row),
1163114402Sru	  row_top_reg(start_row));
1164114402Sru  const char *offset_table[3];
1165114402Sru  if (is_double) {
1166114402Sru    offset_table[0] = "-" HALF_DOUBLE_LINE_SEP;
1167114402Sru    offset_table[1] = "+" HALF_DOUBLE_LINE_SEP;
1168114402Sru    offset_table[2] = 0;
1169114402Sru  }
1170114402Sru  else {
1171114402Sru    offset_table[0] = "";
1172114402Sru    offset_table[1] = 0;
1173114402Sru  }
1174114402Sru  for (const char **offsetp = offset_table; *offsetp; offsetp++) {
1175114402Sru    prints("\\*[" TRANSPARENT_STRING_NAME "].sp -1\n"
1176114402Sru	   "\\*[" TRANSPARENT_STRING_NAME "]\\v'" BODY_DEPTH);
1177114402Sru    if (!bot_adjust.empty())
1178114402Sru      printfs("+%1", bot_adjust);
1179114402Sru    prints("'");
1180114402Sru    printfs("\\h'\\n[%1]u%3'"
1181114402Sru	    "\\s[\\n[" LINESIZE_REG "]]"
1182114402Sru	    "\\D'l 0 |\\*[" QUOTE_STRING_NAME "]\\n[%2]u-1v",
1183114402Sru	    column_divide_reg(col),
1184114402Sru	    row_top_reg(start_row),
1185114402Sru	    *offsetp);
1186114402Sru    if (!bot_adjust.empty())
1187114402Sru      printfs("-(%1)", bot_adjust);
1188114402Sru    // don't perform the top adjustment if the top is actually #T
1189114402Sru    if (!top_adjust.empty())
1190114402Sru      printfs("+((%1)*(%2>\\*[" QUOTE_STRING_NAME "]\\n["
1191114402Sru	      LAST_PASSED_ROW_REG "]))",
1192114402Sru	      top_adjust,
1193114402Sru	      as_string(start_row));
1194114402Sru    prints("'"
1195114402Sru	   "\\s0\n");
1196114402Sru  }
1197114402Sru}
1198114402Sru
1199114402Srutable::table(int nc, unsigned f, int ls, char dpc)
1200114402Sru: flags(f), nrows(0), ncolumns(nc), linesize(ls), decimal_point_char(dpc),
1201114402Sru  vrule_list(0), stuff_list(0), span_list(0),
1202114402Sru  entry_list(0), entry_list_tailp(&entry_list), entry(0),
1203114402Sru  vline(0), row_is_all_lines(0), left_separation(0), right_separation(0),
1204114402Sru  allocated_rows(0)
1205114402Sru{
1206114402Sru  minimum_width = new string[ncolumns];
1207114402Sru  column_separation = ncolumns > 1 ? new int[ncolumns - 1] : 0;
1208114402Sru  equal = new char[ncolumns];
1209114402Sru  int i;
1210114402Sru  for (i = 0; i < ncolumns; i++)
1211114402Sru    equal[i] = 0;
1212114402Sru  for (i = 0; i < ncolumns-1; i++)
1213114402Sru    column_separation[i] = DEFAULT_COLUMN_SEPARATION;
1214114402Sru  delim[0] = delim[1] = '\0';
1215114402Sru}
1216114402Sru
1217114402Srutable::~table()
1218114402Sru{
1219114402Sru  for (int i = 0; i < nrows; i++) {
1220114402Sru    a_delete entry[i];
1221114402Sru    a_delete vline[i];
1222114402Sru  }
1223114402Sru  a_delete entry;
1224114402Sru  a_delete vline;
1225114402Sru  while (entry_list) {
1226114402Sru    table_entry *tem = entry_list;
1227114402Sru    entry_list = entry_list->next;
1228114402Sru    delete tem;
1229114402Sru  }
1230114402Sru  ad_delete(ncolumns) minimum_width;
1231114402Sru  a_delete column_separation;
1232114402Sru  a_delete equal;
1233114402Sru  while (stuff_list) {
1234114402Sru    stuff *tem = stuff_list;
1235114402Sru    stuff_list = stuff_list->next;
1236114402Sru    delete tem;
1237114402Sru  }
1238114402Sru  while (vrule_list) {
1239114402Sru    vertical_rule *tem = vrule_list;
1240114402Sru    vrule_list = vrule_list->next;
1241114402Sru    delete tem;
1242114402Sru  }
1243114402Sru  a_delete row_is_all_lines;
1244114402Sru  while (span_list) {
1245114402Sru    horizontal_span *tem = span_list;
1246114402Sru    span_list = span_list->next;
1247114402Sru    delete tem;
1248114402Sru  }
1249114402Sru}
1250114402Sru
1251114402Sruvoid table::set_delim(char c1, char c2)
1252114402Sru{
1253114402Sru  delim[0] = c1;
1254114402Sru  delim[1] = c2;
1255114402Sru}
1256114402Sru
1257114402Sruvoid table::set_minimum_width(int c, const string &w)
1258114402Sru{
1259114402Sru  assert(c >= 0 && c < ncolumns);
1260114402Sru  minimum_width[c] = w;
1261114402Sru}
1262114402Sru
1263114402Sruvoid table::set_column_separation(int c, int n)
1264114402Sru{
1265114402Sru  assert(c >= 0 && c < ncolumns - 1);
1266114402Sru  column_separation[c] = n;
1267114402Sru}
1268114402Sru
1269114402Sruvoid table::set_equal_column(int c)
1270114402Sru{
1271114402Sru  assert(c >= 0 && c < ncolumns);
1272114402Sru  equal[c] = 1;
1273114402Sru}
1274114402Sru
1275114402Sruvoid table::add_stuff(stuff *p)
1276114402Sru{
1277114402Sru  stuff **pp;
1278114402Sru  for (pp = &stuff_list; *pp; pp = &(*pp)->next)
1279114402Sru    ;
1280114402Sru  *pp = p;
1281114402Sru}
1282114402Sru
1283114402Sruvoid table::add_text_line(int r, const string &s, const char *filename, int lineno)
1284114402Sru{
1285114402Sru  add_stuff(new text_stuff(s, r, filename, lineno));
1286114402Sru}
1287114402Sru
1288114402Sruvoid table::add_single_hline(int r)
1289114402Sru{
1290114402Sru  add_stuff(new single_hline_stuff(r));
1291114402Sru}
1292114402Sru
1293114402Sruvoid table::add_double_hline(int r)
1294114402Sru{
1295114402Sru  add_stuff(new double_hline_stuff(r));
1296114402Sru}
1297114402Sru
1298114402Sruvoid table::allocate(int r)
1299114402Sru{
1300114402Sru  if (r >= nrows) {
1301114402Sru    typedef table_entry **PPtable_entry; // work around g++ 1.36.1 bug
1302114402Sru    if (r >= allocated_rows) {
1303114402Sru      if (allocated_rows == 0) {
1304114402Sru	allocated_rows = 16;
1305114402Sru	if (allocated_rows <= r)
1306114402Sru	  allocated_rows = r + 1;
1307114402Sru	entry = new PPtable_entry[allocated_rows];
1308114402Sru	vline = new char*[allocated_rows];
1309114402Sru      }
1310114402Sru      else {
1311114402Sru	table_entry ***old_entry = entry;
1312114402Sru	int old_allocated_rows = allocated_rows;
1313114402Sru	allocated_rows *= 2;
1314114402Sru	if (allocated_rows <= r)
1315114402Sru	  allocated_rows = r + 1;
1316114402Sru	entry = new PPtable_entry[allocated_rows];
1317114402Sru	memcpy(entry, old_entry, sizeof(table_entry**)*old_allocated_rows);
1318114402Sru	a_delete old_entry;
1319114402Sru	char **old_vline = vline;
1320114402Sru	vline = new char*[allocated_rows];
1321114402Sru	memcpy(vline, old_vline, sizeof(char*)*old_allocated_rows);
1322114402Sru	a_delete old_vline;
1323114402Sru      }
1324114402Sru    }
1325114402Sru    assert(allocated_rows > r);
1326114402Sru    while (nrows <= r) {
1327114402Sru      entry[nrows] = new table_entry*[ncolumns];
1328114402Sru      int i;
1329114402Sru      for (i = 0; i < ncolumns; i++)
1330114402Sru	entry[nrows][i] = 0;
1331114402Sru      vline[nrows] = new char[ncolumns+1];
1332114402Sru      for (i = 0; i < ncolumns+1; i++)
1333114402Sru	vline[nrows][i] = 0;
1334114402Sru      nrows++;
1335114402Sru    }
1336114402Sru  }
1337114402Sru}
1338114402Sru
1339114402Sruvoid table::do_hspan(int r, int c)
1340114402Sru{
1341114402Sru  assert(r >= 0 && c >= 0 && r < nrows && c < ncolumns);
1342114402Sru  if (c == 0) {
1343114402Sru    error("first column cannot be horizontally spanned");
1344114402Sru    return;
1345114402Sru  }
1346114402Sru  table_entry *e = entry[r][c];
1347114402Sru  if (e) {
1348114402Sru    assert(e->start_row <= r && r <= e->end_row
1349114402Sru	   && e->start_col <= c && c <= e->end_col
1350114402Sru	   && e->end_row - e->start_row > 0
1351114402Sru	   && e->end_col - e->start_col > 0);
1352114402Sru    return;
1353114402Sru  }
1354114402Sru  e = entry[r][c-1];
1355114402Sru  // e can be 0 if we had an empty entry or an error
1356114402Sru  if (e == 0)
1357114402Sru    return;
1358114402Sru  if (e->start_row != r) {
1359114402Sru    /*
1360114402Sru      l l
1361114402Sru      ^ s */
1362114402Sru    error("impossible horizontal span at row %1, column %2", r + 1, c + 1);
1363114402Sru  }
1364114402Sru  else {
1365114402Sru    e->end_col = c;
1366114402Sru    entry[r][c] = e;
1367114402Sru  }
1368114402Sru}
1369114402Sru
1370114402Sruvoid table::do_vspan(int r, int c)
1371114402Sru{
1372114402Sru  assert(r >= 0 && c >= 0 && r < nrows && c < ncolumns);
1373114402Sru  if (r == 0) {
1374114402Sru    error("first row cannot be vertically spanned");
1375114402Sru    return;
1376114402Sru  }
1377114402Sru  table_entry *e = entry[r][c];
1378114402Sru  if (e) {
1379114402Sru    assert(e->start_row <= r && r <= e->end_row
1380114402Sru	   && e->start_col <= c && c <= e->end_col
1381114402Sru	   && e->end_row - e->start_row > 0
1382114402Sru	   && e->end_col - e->start_col > 0);
1383114402Sru    return;
1384114402Sru  }
1385114402Sru  e = entry[r-1][c];
1386114402Sru  // e can be 0 if we had an empty entry or an error
1387114402Sru  if (e == 0)
1388114402Sru    return;
1389114402Sru  if (e->start_col != c) {
1390114402Sru    /* l s
1391114402Sru       l ^ */
1392114402Sru    error("impossible vertical span at row %1, column %2", r + 1, c + 1);
1393114402Sru  }
1394114402Sru  else {
1395114402Sru    for (int i = c; i <= e->end_col; i++) {
1396114402Sru      assert(entry[r][i] == 0);
1397114402Sru      entry[r][i] = e;
1398114402Sru    }
1399114402Sru    e->end_row = r;
1400114402Sru  }
1401114402Sru}
1402114402Sru
1403114402Sruint find_decimal_point(const char *s, char decimal_point_char,
1404114402Sru		       const char *delim)
1405114402Sru{
1406114402Sru  if (s == 0 || *s == '\0')
1407114402Sru    return -1;
1408114402Sru  const char *p;
1409114402Sru  int in_delim = 0;		// is p within eqn delimiters?
1410114402Sru  // tbl recognises \& even within eqn delimiters; I don't
1411114402Sru  for (p = s; *p; p++)
1412114402Sru    if (in_delim) {
1413114402Sru      if (*p == delim[1])
1414114402Sru	in_delim = 0;
1415114402Sru    }
1416114402Sru    else if (*p == delim[0])
1417114402Sru      in_delim = 1;
1418114402Sru    else if (p[0] == '\\' && p[1] == '&')
1419114402Sru      return p - s;
1420114402Sru  int possible_pos = -1;
1421114402Sru  in_delim = 0;
1422114402Sru  for (p = s; *p; p++)
1423114402Sru    if (in_delim) {
1424114402Sru      if (*p == delim[1])
1425114402Sru	in_delim = 0;
1426114402Sru    }
1427114402Sru    else if (*p == delim[0])
1428114402Sru      in_delim = 1;
1429114402Sru    else if (p[0] == decimal_point_char && csdigit(p[1]))
1430114402Sru      possible_pos = p - s;
1431114402Sru  if (possible_pos >= 0)
1432114402Sru    return possible_pos;
1433114402Sru  in_delim = 0;
1434114402Sru  for (p = s; *p; p++)
1435114402Sru    if (in_delim) {
1436114402Sru      if (*p == delim[1])
1437114402Sru	in_delim = 0;
1438114402Sru    }
1439114402Sru    else if (*p == delim[0])
1440114402Sru      in_delim = 1;
1441114402Sru    else if (csdigit(*p))
1442114402Sru      possible_pos = p + 1 - s;
1443114402Sru  return possible_pos;
1444114402Sru}
1445114402Sru
1446114402Sruvoid table::add_entry(int r, int c, const string &str, const entry_format *f,
1447114402Sru		      const char *fn, int ln)
1448114402Sru{
1449114402Sru  allocate(r);
1450114402Sru  table_entry *e = 0;
1451114402Sru  if (str == "\\_") {
1452114402Sru    e = new short_line_entry(f);
1453114402Sru  }
1454114402Sru  else if (str == "\\=") {
1455114402Sru    e = new short_double_line_entry(f);
1456114402Sru  }
1457114402Sru  else if (str == "_") {
1458114402Sru    single_line_entry *lefte;
1459114402Sru    if (c > 0 && entry[r][c-1] != 0 &&
1460114402Sru	(lefte = entry[r][c-1]->to_single_line_entry()) != 0
1461114402Sru	&& lefte->start_row == r
1462114402Sru	&& lefte->mod->stagger == f->stagger) {
1463114402Sru      lefte->end_col = c;
1464114402Sru      entry[r][c] = lefte;
1465114402Sru    }
1466114402Sru    else
1467114402Sru      e = new single_line_entry(f);
1468114402Sru  }
1469114402Sru  else if (str == "=") {
1470114402Sru    double_line_entry *lefte;
1471114402Sru    if (c > 0 && entry[r][c-1] != 0 &&
1472114402Sru	(lefte = entry[r][c-1]->to_double_line_entry()) != 0
1473114402Sru	&& lefte->start_row == r
1474114402Sru	&& lefte->mod->stagger == f->stagger) {
1475114402Sru      lefte->end_col = c;
1476114402Sru      entry[r][c] = lefte;
1477114402Sru    }
1478114402Sru    else
1479114402Sru      e = new double_line_entry(f);
1480114402Sru  }
1481114402Sru  else if (str == "\\^") {
1482114402Sru    do_vspan(r, c);
1483114402Sru  }
1484114402Sru  else if (str.length() > 2 && str[0] == '\\' && str[1] == 'R') {
1485114402Sru    if (str.search('\n') >= 0)
1486114402Sru      error_with_file_and_line(fn, ln, "bad repeated character");
1487114402Sru    else {
1488114402Sru      char *s = str.substring(2, str.length() - 2).extract();
1489114402Sru      e = new repeated_char_entry(s, f);
1490114402Sru    }
1491114402Sru  }
1492114402Sru  else {
1493114402Sru    int is_block = str.search('\n') >= 0;
1494114402Sru    char *s;
1495114402Sru    switch (f->type) {
1496114402Sru    case FORMAT_SPAN:
1497114402Sru      assert(str.empty());
1498114402Sru      do_hspan(r, c);
1499114402Sru      break;
1500114402Sru    case FORMAT_LEFT:
1501114402Sru      if (!str.empty()) {
1502114402Sru	s = str.extract();
1503114402Sru	if (is_block)
1504114402Sru	  e = new left_block_entry(s, f);
1505114402Sru	else
1506114402Sru	  e = new left_text_entry(s, f);
1507114402Sru      }
1508114402Sru      else
1509114402Sru	e = new empty_entry(f);
1510114402Sru      break;
1511114402Sru    case FORMAT_CENTER:
1512114402Sru      if (!str.empty()) {
1513114402Sru	s = str.extract();
1514114402Sru	if (is_block)
1515114402Sru	  e = new center_block_entry(s, f);
1516114402Sru	else
1517114402Sru	  e = new center_text_entry(s, f);
1518114402Sru      }
1519114402Sru      else
1520114402Sru	e = new empty_entry(f);
1521114402Sru      break;
1522114402Sru    case FORMAT_RIGHT:
1523114402Sru      if (!str.empty()) {
1524114402Sru	s = str.extract();
1525114402Sru	if (is_block)
1526114402Sru	  e = new right_block_entry(s, f);
1527114402Sru	else
1528114402Sru	  e = new right_text_entry(s, f);
1529114402Sru      }
1530114402Sru      else
1531114402Sru	e = new empty_entry(f);
1532114402Sru      break;
1533114402Sru    case FORMAT_NUMERIC:
1534114402Sru      if (!str.empty()) {
1535114402Sru	s = str.extract();
1536114402Sru	if (is_block) {
1537114402Sru	  error_with_file_and_line(fn, ln, "can't have numeric text block");
1538114402Sru	  e = new left_block_entry(s, f);
1539114402Sru	}
1540114402Sru	else {
1541114402Sru	  int pos = find_decimal_point(s, decimal_point_char, delim);
1542114402Sru	  if (pos < 0)
1543114402Sru	    e = new center_text_entry(s, f);
1544114402Sru	  else
1545114402Sru	    e = new numeric_text_entry(s, f, pos);
1546114402Sru	}
1547114402Sru      }
1548114402Sru      else
1549114402Sru	e = new empty_entry(f);
1550114402Sru      break;
1551114402Sru    case FORMAT_ALPHABETIC:
1552114402Sru      if (!str.empty()) {
1553114402Sru	s = str.extract();
1554114402Sru	if (is_block)
1555114402Sru	  e = new alphabetic_block_entry(s, f);
1556114402Sru	else
1557114402Sru	  e = new alphabetic_text_entry(s, f);
1558114402Sru      }
1559114402Sru      else
1560114402Sru	e = new empty_entry(f);
1561114402Sru      break;
1562114402Sru    case FORMAT_VSPAN:
1563114402Sru      do_vspan(r, c);
1564114402Sru      break;
1565114402Sru    case FORMAT_HLINE:
1566114402Sru      if (str.length() != 0)
1567114402Sru	error_with_file_and_line(fn, ln,
1568114402Sru				 "non-empty data entry for `_' format ignored");
1569114402Sru      e = new single_line_entry(f);
1570114402Sru      break;
1571114402Sru    case FORMAT_DOUBLE_HLINE:
1572114402Sru      if (str.length() != 0)
1573114402Sru	error_with_file_and_line(fn, ln,
1574114402Sru				 "non-empty data entry for `=' format ignored");
1575114402Sru      e = new double_line_entry(f);
1576114402Sru      break;
1577114402Sru    default:
1578114402Sru      assert(0);
1579114402Sru    }
1580114402Sru  }
1581114402Sru  if (e) {
1582114402Sru    table_entry *preve = entry[r][c];
1583114402Sru    if (preve) {
1584114402Sru      /* c s
1585114402Sru         ^ l */
1586114402Sru      error_with_file_and_line(fn, ln, "row %1, column %2 already spanned",
1587114402Sru			       r + 1, c + 1);
1588114402Sru      delete e;
1589114402Sru    }
1590114402Sru    else {
1591114402Sru      e->input_lineno = ln;
1592114402Sru      e->input_filename = fn;
1593114402Sru      e->start_row = e->end_row = r;
1594114402Sru      e->start_col = e->end_col = c;
1595114402Sru      *entry_list_tailp = e;
1596114402Sru      entry_list_tailp = &e->next;
1597114402Sru      entry[r][c] = e;
1598114402Sru    }
1599114402Sru  }
1600114402Sru}
1601114402Sru
1602114402Sru// add vertical lines for row r
1603114402Sru
1604114402Sruvoid table::add_vlines(int r, const char *v)
1605114402Sru{
1606114402Sru  allocate(r);
1607114402Sru  for (int i = 0; i < ncolumns+1; i++)
1608114402Sru    vline[r][i] = v[i];
1609114402Sru}
1610114402Sru
1611114402Sruvoid table::check()
1612114402Sru{
1613114402Sru  table_entry *p = entry_list;
1614114402Sru  int i, j;
1615114402Sru  while (p) {
1616114402Sru    for (i = p->start_row; i <= p->end_row; i++)
1617114402Sru      for (j = p->start_col; j <= p->end_col; j++)
1618114402Sru	assert(entry[i][j] == p);
1619114402Sru    p = p->next;
1620114402Sru  }
1621114402Sru}
1622114402Sru
1623114402Sruvoid table::print()
1624114402Sru{
1625114402Sru  location_force_filename = 1;
1626114402Sru  check();
1627114402Sru  init_output();
1628114402Sru  determine_row_type();
1629114402Sru  compute_widths();
1630114402Sru  if (!(flags & CENTER))
1631114402Sru    prints(".if \\n[" SAVED_CENTER_REG "] \\{");
1632114402Sru  prints(".in +(u;\\n[.l]-\\n[.i]-\\n[TW]/2>?-\\n[.i])\n"
1633114402Sru	 ".nr " SAVED_INDENT_REG " \\n[.i]\n");
1634114402Sru  if (!(flags & CENTER))
1635114402Sru    prints(".\\}\n");
1636114402Sru  build_vrule_list();
1637114402Sru  define_bottom_macro();
1638114402Sru  do_top();
1639114402Sru  for (int i = 0; i < nrows; i++)
1640114402Sru    do_row(i);
1641114402Sru  do_bottom();
1642114402Sru}
1643114402Sru
1644114402Sruvoid table::determine_row_type()
1645114402Sru{
1646114402Sru  row_is_all_lines = new char[nrows];
1647114402Sru  for (int i = 0; i < nrows; i++) {
1648114402Sru    int had_single = 0;
1649114402Sru    int had_double = 0;
1650114402Sru    int had_non_line = 0;
1651114402Sru    for (int c = 0; c < ncolumns; c++) {
1652114402Sru      table_entry *e = entry[i][c];
1653114402Sru      if (e != 0) {
1654114402Sru	if (e->start_row == e->end_row) {
1655114402Sru	  int t = e->line_type();
1656114402Sru	  switch (t) {
1657114402Sru	  case -1:
1658114402Sru	    had_non_line = 1;
1659114402Sru	    break;
1660114402Sru	  case 0:
1661114402Sru	    // empty
1662114402Sru	    break;
1663114402Sru	  case 1:
1664114402Sru	    had_single = 1;
1665114402Sru	    break;
1666114402Sru	  case 2:
1667114402Sru	    had_double = 1;
1668114402Sru	    break;
1669114402Sru	  default:
1670114402Sru	    assert(0);
1671114402Sru	  }
1672114402Sru	  if (had_non_line)
1673114402Sru	    break;
1674114402Sru	}
1675114402Sru	c = e->end_col;
1676114402Sru      }
1677114402Sru    }
1678114402Sru    if (had_non_line)
1679114402Sru      row_is_all_lines[i] = 0;
1680114402Sru    else if (had_double)
1681114402Sru      row_is_all_lines[i] = 2;
1682114402Sru    else if (had_single)
1683114402Sru      row_is_all_lines[i] = 1;
1684114402Sru    else
1685114402Sru      row_is_all_lines[i] = 0;
1686114402Sru  }
1687114402Sru}
1688114402Sru
1689114402Sruvoid table::init_output()
1690114402Sru{
1691114402Sru  prints(".nr " COMPATIBLE_REG " \\n(.C\n"
1692114402Sru	 ".cp 0\n");
1693114402Sru  if (linesize > 0)
1694114402Sru    printfs(".nr " LINESIZE_REG " %1\n", as_string(linesize));
1695114402Sru  else
1696114402Sru    prints(".nr " LINESIZE_REG " \\n[.s]\n");
1697114402Sru  if (!(flags & CENTER))
1698114402Sru    prints(".nr " SAVED_CENTER_REG " \\n[.ce]\n");
1699151497Sru  if (compatible_flag)
1700151497Sru    prints(".ds " LEADER_REG " \\a\n");
1701114402Sru  prints(".de " RESET_MACRO_NAME "\n"
1702114402Sru	 ".ft \\n[.f]\n"
1703114402Sru	 ".ps \\n[.s]\n"
1704114402Sru	 ".vs \\n[.v]u\n"
1705114402Sru	 ".in \\n[.i]u\n"
1706114402Sru	 ".ll \\n[.l]u\n"
1707114402Sru	 ".ls \\n[.L]\n"
1708114402Sru	 ".ad \\n[.j]\n"
1709114402Sru	 ".ie \\n[.u] .fi\n"
1710114402Sru	 ".el .nf\n"
1711114402Sru	 ".ce \\n[.ce]\n"
1712114402Sru	 "..\n"
1713114402Sru	 ".nr " SAVED_INDENT_REG " \\n[.i]\n"
1714114402Sru	 ".nr " SAVED_FONT_REG " \\n[.f]\n"
1715114402Sru	 ".nr " SAVED_SIZE_REG " \\n[.s]\n"
1716114402Sru	 ".nr " SAVED_FILL_REG " \\n[.u]\n"
1717114402Sru	 ".nr T. 0\n"
1718114402Sru	 ".nr " CURRENT_ROW_REG " 0-1\n"
1719114402Sru	 ".nr " LAST_PASSED_ROW_REG " 0-1\n"
1720114402Sru	 ".nr " SECTION_DIVERSION_FLAG_REG " 0\n"
1721114402Sru	 ".ds " TRANSPARENT_STRING_NAME "\n"
1722114402Sru	 ".ds " QUOTE_STRING_NAME "\n"
1723114402Sru	 ".nr " NEED_BOTTOM_RULE_REG " 1\n"
1724114402Sru	 ".nr " SUPPRESS_BOTTOM_REG " 0\n"
1725114402Sru	 ".eo\n"
1726114402Sru	 ".de " REPEATED_MARK_MACRO "\n"
1727114402Sru	 ".mk \\$1\n"
1728114402Sru	 ".if !'\\n(.z'' \\!." REPEATED_MARK_MACRO " \"\\$1\"\n"
1729114402Sru	 "..\n"
1730114402Sru	 ".de " REPEATED_VPT_MACRO "\n"
1731114402Sru	 ".vpt \\$1\n"
1732114402Sru	 ".if !'\\n(.z'' \\!." REPEATED_VPT_MACRO " \"\\$1\"\n"
1733114402Sru	 "..\n");
1734114402Sru  if (!(flags & NOKEEP))
1735114402Sru    prints(".de " KEEP_MACRO_NAME "\n"
1736114402Sru	   ".if '\\n[.z]'' \\{.ds " QUOTE_STRING_NAME " \\\\\n"
1737114402Sru	   ".ds " TRANSPARENT_STRING_NAME " \\!\n"
1738114402Sru	   ".di " SECTION_DIVERSION_NAME "\n"
1739114402Sru	   ".nr " SECTION_DIVERSION_FLAG_REG " 1\n"
1740114402Sru	   ".in 0\n"
1741114402Sru	   ".\\}\n"
1742114402Sru	   "..\n"
1743114402Sru	   ".de " RELEASE_MACRO_NAME "\n"
1744114402Sru	   ".if \\n[" SECTION_DIVERSION_FLAG_REG "] \\{"
1745114402Sru	   ".di\n"
1746114402Sru	   ".in \\n[" SAVED_INDENT_REG "]u\n"
1747114402Sru	   ".nr " SAVED_DN_REG " \\n[dn]\n"
1748114402Sru	   ".ds " QUOTE_STRING_NAME "\n"
1749114402Sru	   ".ds " TRANSPARENT_STRING_NAME "\n"
1750114402Sru	   ".nr " SECTION_DIVERSION_FLAG_REG " 0\n"
1751114402Sru	   ".if \\n[.t]<=\\n[dn] \\{"
1752114402Sru	   ".nr T. 1\n"
1753114402Sru	   ".T#\n"
1754114402Sru	   ".nr " SUPPRESS_BOTTOM_REG " 1\n"
1755114402Sru	   ".sp \\n[.t]u\n"
1756114402Sru	   ".nr " SUPPRESS_BOTTOM_REG " 0\n"
1757114402Sru	   ".mk #T\n"
1758114402Sru	   ".\\}\n"
1759114402Sru	   ".if \\n[.t]<=\\n[" SAVED_DN_REG "] "
1760114402Sru	   /* Since we turn off traps, it won't get into an infinite loop
1761114402Sru	   when we try and print it; it will just go off the bottom of the
1762114402Sru	   page. */
1763114402Sru	   ".tm warning: page \\n%: table text block will not fit on one page\n"
1764114402Sru	   ".nf\n"
1765114402Sru	   ".ls 1\n"
1766114402Sru	   "." SECTION_DIVERSION_NAME "\n"
1767114402Sru	   ".ls\n"
1768114402Sru	   ".rm " SECTION_DIVERSION_NAME "\n"
1769114402Sru	   ".\\}\n"
1770114402Sru	   "..\n"
1771114402Sru	   ".nr " TABLE_DIVERSION_FLAG_REG " 0\n"
1772114402Sru	   ".de " TABLE_KEEP_MACRO_NAME "\n"
1773114402Sru	   ".if '\\n[.z]'' \\{"
1774114402Sru	   ".di " TABLE_DIVERSION_NAME "\n"
1775114402Sru	   ".nr " TABLE_DIVERSION_FLAG_REG " 1\n"
1776114402Sru	   ".\\}\n"
1777114402Sru	   "..\n"
1778114402Sru	   ".de " TABLE_RELEASE_MACRO_NAME "\n"
1779114402Sru	   ".if \\n[" TABLE_DIVERSION_FLAG_REG "] \\{.br\n"
1780114402Sru	   ".di\n"
1781114402Sru	   ".nr " SAVED_DN_REG " \\n[dn]\n"
1782114402Sru	   ".ne \\n[dn]u+\\n[.V]u\n"
1783114402Sru	   ".ie \\n[.t]<=\\n[" SAVED_DN_REG "] "
1784114402Sru	   ".tm error: page \\n%: table will not fit on one page; use .TS H/.TH with a supporting macro package\n"
1785114402Sru	   ".el \\{"
1786114402Sru	   ".in 0\n"
1787114402Sru	   ".ls 1\n"
1788114402Sru	   ".nf\n"
1789114402Sru	   "." TABLE_DIVERSION_NAME "\n"
1790114402Sru	   ".\\}\n"
1791114402Sru	   ".rm " TABLE_DIVERSION_NAME "\n"
1792114402Sru	   ".\\}\n"
1793114402Sru	   "..\n");
1794114402Sru  prints(".ec\n"
1795114402Sru	 ".ce 0\n"
1796114402Sru	 ".nf\n");
1797114402Sru}
1798114402Sru
1799114402Srustring block_width_reg(int r, int c)
1800114402Sru{
1801114402Sru  static char name[sizeof(BLOCK_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1802114402Sru  sprintf(name, BLOCK_WIDTH_PREFIX "%d,%d", r, c);
1803114402Sru  return string(name);
1804114402Sru}
1805114402Sru
1806114402Srustring block_diversion_name(int r, int c)
1807114402Sru{
1808114402Sru  static char name[sizeof(BLOCK_DIVERSION_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1809114402Sru  sprintf(name, BLOCK_DIVERSION_PREFIX "%d,%d", r, c);
1810114402Sru  return string(name);
1811114402Sru}
1812114402Sru
1813114402Srustring block_height_reg(int r, int c)
1814114402Sru{
1815114402Sru  static char name[sizeof(BLOCK_HEIGHT_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1816114402Sru  sprintf(name, BLOCK_HEIGHT_PREFIX "%d,%d", r, c);
1817114402Sru  return string(name);
1818114402Sru}
1819114402Sru
1820114402Srustring span_width_reg(int start_col, int end_col)
1821114402Sru{
1822114402Sru  static char name[sizeof(SPAN_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1823114402Sru  sprintf(name, SPAN_WIDTH_PREFIX "%d", start_col);
1824114402Sru  if (end_col != start_col)
1825114402Sru    sprintf(strchr(name, '\0'), ",%d", end_col);
1826114402Sru  return string(name);
1827114402Sru}
1828114402Sru
1829114402Srustring span_left_numeric_width_reg(int start_col, int end_col)
1830114402Sru{
1831114402Sru  static char name[sizeof(SPAN_LEFT_NUMERIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1832114402Sru  sprintf(name, SPAN_LEFT_NUMERIC_WIDTH_PREFIX "%d", start_col);
1833114402Sru  if (end_col != start_col)
1834114402Sru    sprintf(strchr(name, '\0'), ",%d", end_col);
1835114402Sru  return string(name);
1836114402Sru}
1837114402Sru
1838114402Srustring span_right_numeric_width_reg(int start_col, int end_col)
1839114402Sru{
1840114402Sru  static char name[sizeof(SPAN_RIGHT_NUMERIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1841114402Sru  sprintf(name, SPAN_RIGHT_NUMERIC_WIDTH_PREFIX "%d", start_col);
1842114402Sru  if (end_col != start_col)
1843114402Sru    sprintf(strchr(name, '\0'), ",%d", end_col);
1844114402Sru  return string(name);
1845114402Sru}
1846114402Sru
1847114402Srustring span_alphabetic_width_reg(int start_col, int end_col)
1848114402Sru{
1849114402Sru  static char name[sizeof(SPAN_ALPHABETIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1850114402Sru  sprintf(name, SPAN_ALPHABETIC_WIDTH_PREFIX "%d", start_col);
1851114402Sru  if (end_col != start_col)
1852114402Sru    sprintf(strchr(name, '\0'), ",%d", end_col);
1853114402Sru  return string(name);
1854114402Sru}
1855114402Sru
1856114402Srustring column_separation_reg(int col)
1857114402Sru{
1858114402Sru  static char name[sizeof(COLUMN_SEPARATION_PREFIX)+INT_DIGITS];
1859114402Sru  sprintf(name, COLUMN_SEPARATION_PREFIX "%d", col);
1860114402Sru  return string(name);
1861114402Sru}
1862114402Sru
1863114402Srustring row_start_reg(int row)
1864114402Sru{
1865114402Sru  static char name[sizeof(ROW_START_PREFIX)+INT_DIGITS];
1866114402Sru  sprintf(name, ROW_START_PREFIX "%d", row);
1867114402Sru  return string(name);
1868114402Sru}
1869114402Sru
1870114402Srustring column_start_reg(int col)
1871114402Sru{
1872114402Sru  static char name[sizeof(COLUMN_START_PREFIX)+INT_DIGITS];
1873114402Sru  sprintf(name, COLUMN_START_PREFIX "%d", col);
1874114402Sru  return string(name);
1875114402Sru}
1876114402Sru
1877114402Srustring column_end_reg(int col)
1878114402Sru{
1879114402Sru  static char name[sizeof(COLUMN_END_PREFIX)+INT_DIGITS];
1880114402Sru  sprintf(name, COLUMN_END_PREFIX "%d", col);
1881114402Sru  return string(name);
1882114402Sru}
1883114402Sru
1884114402Srustring column_divide_reg(int col)
1885114402Sru{
1886114402Sru  static char name[sizeof(COLUMN_DIVIDE_PREFIX)+INT_DIGITS];
1887114402Sru  sprintf(name, COLUMN_DIVIDE_PREFIX "%d", col);
1888114402Sru  return string(name);
1889114402Sru}
1890114402Sru
1891114402Srustring row_top_reg(int row)
1892114402Sru{
1893114402Sru  static char name[sizeof(ROW_TOP_PREFIX)+INT_DIGITS];
1894114402Sru  sprintf(name, ROW_TOP_PREFIX "%d", row);
1895114402Sru  return string(name);
1896114402Sru}
1897114402Sru
1898114402Sruvoid init_span_reg(int start_col, int end_col)
1899114402Sru{
1900114402Sru  printfs(".nr %1 \\n(.H\n.nr %2 0\n.nr %3 0\n.nr %4 0\n",
1901114402Sru	  span_width_reg(start_col, end_col),
1902114402Sru	  span_alphabetic_width_reg(start_col, end_col),
1903114402Sru	  span_left_numeric_width_reg(start_col, end_col),
1904114402Sru	  span_right_numeric_width_reg(start_col, end_col));
1905114402Sru}
1906114402Sru
1907114402Sruvoid compute_span_width(int start_col, int end_col)
1908114402Sru{
1909114402Sru  printfs(".nr %1 \\n[%1]>?(\\n[%2]+\\n[%3])\n"
1910114402Sru	  ".if \\n[%4] .nr %1 \\n[%1]>?(\\n[%4]+2n)\n",
1911114402Sru	  span_width_reg(start_col, end_col),
1912114402Sru	  span_left_numeric_width_reg(start_col, end_col),
1913114402Sru	  span_right_numeric_width_reg(start_col, end_col),
1914114402Sru	  span_alphabetic_width_reg(start_col, end_col));
1915114402Sru}
1916114402Sru
1917114402Sru// Increase the widths of columns so that the width of any spanning entry
1918114402Sru// is not greater than the sum of the widths of the columns that it spans.
1919114402Sru// Ensure that the widths of columns remain equal.
1920114402Sru
1921114402Sruvoid table::divide_span(int start_col, int end_col)
1922114402Sru{
1923114402Sru  assert(end_col > start_col);
1924114402Sru  printfs(".nr " NEEDED_REG " \\n[%1]-(\\n[%2]",
1925114402Sru	  span_width_reg(start_col, end_col),
1926114402Sru	  span_width_reg(start_col, start_col));
1927114402Sru  int i;
1928114402Sru  for (i = start_col + 1; i <= end_col; i++) {
1929114402Sru    // The column separation may shrink with the expand option.
1930114402Sru    if (!(flags & EXPAND))
1931114402Sru      printfs("+%1n", as_string(column_separation[i - 1]));
1932114402Sru    printfs("+\\n[%1]", span_width_reg(i, i));
1933114402Sru  }
1934114402Sru  prints(")\n");
1935114402Sru  printfs(".nr " NEEDED_REG " \\n[" NEEDED_REG "]/%1\n",
1936114402Sru	  as_string(end_col - start_col + 1));
1937114402Sru  prints(".if \\n[" NEEDED_REG "] \\{");
1938114402Sru  for (i = start_col; i <= end_col; i++)
1939114402Sru    printfs(".nr %1 +\\n[" NEEDED_REG "]\n",
1940114402Sru	    span_width_reg(i, i));
1941114402Sru  int equal_flag = 0;
1942114402Sru  for (i = start_col; i <= end_col && !equal_flag; i++)
1943114402Sru    if (equal[i])
1944114402Sru      equal_flag = 1;
1945114402Sru  if (equal_flag) {
1946114402Sru    for (i = 0; i < ncolumns; i++)
1947114402Sru      if (i < start_col || i > end_col)
1948114402Sru	printfs(".nr %1 +\\n[" NEEDED_REG "]\n",
1949114402Sru	    span_width_reg(i, i));
1950114402Sru  }
1951114402Sru  prints(".\\}\n");
1952114402Sru}
1953114402Sru
1954114402Sruvoid table::sum_columns(int start_col, int end_col)
1955114402Sru{
1956114402Sru  assert(end_col > start_col);
1957114402Sru  printfs(".nr %1 \\n[%2]",
1958114402Sru	  span_width_reg(start_col, end_col),
1959114402Sru	  span_width_reg(start_col, start_col));
1960114402Sru  for (int i = start_col + 1; i <= end_col; i++)
1961114402Sru    printfs("+(%1*\\n[" SEPARATION_FACTOR_REG "])+\\n[%2]",
1962114402Sru	    as_string(column_separation[i - 1]),
1963114402Sru	    span_width_reg(i, i));
1964114402Sru  prints('\n');
1965114402Sru}
1966114402Sru
1967114402Sruhorizontal_span::horizontal_span(int sc, int ec, horizontal_span *p)
1968114402Sru: next(p), start_col(sc), end_col(ec)
1969114402Sru{
1970114402Sru}
1971114402Sru
1972114402Sruvoid table::build_span_list()
1973114402Sru{
1974114402Sru  span_list = 0;
1975114402Sru  table_entry *p = entry_list;
1976114402Sru  while (p) {
1977114402Sru    if (p->end_col != p->start_col) {
1978114402Sru      horizontal_span *q;
1979114402Sru      for (q = span_list; q; q = q->next)
1980114402Sru	if (q->start_col == p->start_col
1981114402Sru	    && q->end_col == p->end_col)
1982114402Sru	  break;
1983114402Sru      if (!q)
1984114402Sru	span_list = new horizontal_span(p->start_col, p->end_col, span_list);
1985114402Sru    }
1986114402Sru    p = p->next;
1987114402Sru  }
1988114402Sru  // Now sort span_list primarily by order of end_row, and secondarily
1989114402Sru  // by reverse order of start_row. This ensures that if we divide
1990114402Sru  // spans using the order in span_list, we will get reasonable results.
1991114402Sru  horizontal_span *unsorted = span_list;
1992114402Sru  span_list = 0;
1993114402Sru  while (unsorted) {
1994114402Sru    horizontal_span **pp;
1995114402Sru    for (pp = &span_list; *pp; pp = &(*pp)->next)
1996114402Sru      if (unsorted->end_col < (*pp)->end_col
1997114402Sru	  || (unsorted->end_col == (*pp)->end_col
1998114402Sru	      && (unsorted->start_col > (*pp)->start_col)))
1999114402Sru	break;
2000114402Sru    horizontal_span *tem = unsorted->next;
2001114402Sru    unsorted->next = *pp;
2002114402Sru    *pp = unsorted;
2003114402Sru    unsorted = tem;
2004114402Sru  }
2005114402Sru}
2006114402Sru
2007114402Sruvoid table::compute_separation_factor()
2008114402Sru{
2009114402Sru  if (flags & (ALLBOX|BOX|DOUBLEBOX))
2010114402Sru    left_separation = right_separation = 1;
2011114402Sru  else {
2012114402Sru    for (int i = 0; i < nrows; i++) {
2013114402Sru      if (vline[i][0] > 0)
2014114402Sru	left_separation = 1;
2015114402Sru      if (vline[i][ncolumns] > 0)
2016114402Sru	right_separation = 1;
2017114402Sru    }
2018114402Sru  }
2019114402Sru  if (flags & EXPAND) {
2020114402Sru    int total_sep = left_separation + right_separation;
2021114402Sru    int i;
2022114402Sru    for (i = 0; i < ncolumns - 1; i++)
2023114402Sru      total_sep += column_separation[i];
2024114402Sru    if (total_sep != 0) {
2025114402Sru      // Don't let the separation factor be negative.
2026114402Sru      prints(".nr " SEPARATION_FACTOR_REG " \\n[.l]-\\n[.i]");
2027114402Sru      for (i = 0; i < ncolumns; i++)
2028114402Sru	printfs("-\\n[%1]", span_width_reg(i, i));
2029114402Sru      printfs("/%1>?0\n", as_string(total_sep));
2030114402Sru    }
2031114402Sru  }
2032114402Sru}
2033114402Sru
2034114402Sruvoid table::compute_column_positions()
2035114402Sru{
2036114402Sru  printfs(".nr %1 0\n", column_divide_reg(0));
2037114402Sru  printfs(".nr %1 %2*\\n[" SEPARATION_FACTOR_REG "]\n",
2038114402Sru	  column_start_reg(0),
2039114402Sru	  as_string(left_separation));
2040114402Sru  int i;
2041114402Sru  for (i = 1;; i++) {
2042114402Sru    printfs(".nr %1 \\n[%2]+\\n[%3]\n",
2043114402Sru	    column_end_reg(i-1),
2044114402Sru	    column_start_reg(i-1),
2045114402Sru	    span_width_reg(i-1, i-1));
2046114402Sru    if (i >= ncolumns)
2047114402Sru      break;
2048114402Sru    printfs(".nr %1 \\n[%2]+(%3*\\n[" SEPARATION_FACTOR_REG "])\n",
2049114402Sru	    column_start_reg(i),
2050114402Sru	    column_end_reg(i-1),
2051114402Sru	    as_string(column_separation[i-1]));
2052114402Sru    printfs(".nr %1 \\n[%2]+\\n[%3]/2\n",
2053114402Sru	    column_divide_reg(i),
2054114402Sru	    column_end_reg(i-1),
2055114402Sru	    column_start_reg(i));
2056114402Sru  }
2057114402Sru  printfs(".nr %1 \\n[%2]+(%3*\\n[" SEPARATION_FACTOR_REG "])\n",
2058114402Sru	  column_divide_reg(ncolumns),
2059114402Sru	  column_end_reg(i-1),
2060114402Sru	  as_string(right_separation));
2061114402Sru  printfs(".nr TW \\n[%1]\n",
2062114402Sru	  column_divide_reg(ncolumns));
2063114402Sru  if (flags & DOUBLEBOX) {
2064114402Sru    printfs(".nr %1 +" DOUBLE_LINE_SEP "\n", column_divide_reg(0));
2065114402Sru    printfs(".nr %1 -" DOUBLE_LINE_SEP "\n", column_divide_reg(ncolumns));
2066114402Sru  }
2067114402Sru}
2068114402Sru
2069114402Sruvoid table::make_columns_equal()
2070114402Sru{
2071114402Sru  int first = -1;		// index of first equal column
2072114402Sru  int i;
2073114402Sru  for (i = 0; i < ncolumns; i++)
2074114402Sru    if (equal[i]) {
2075114402Sru      if (first < 0) {
2076114402Sru	printfs(".nr %1 \\n[%1]", span_width_reg(i, i));
2077114402Sru	first = i;
2078114402Sru      }
2079114402Sru      else
2080114402Sru	printfs(">?\\n[%1]", span_width_reg(i, i));
2081114402Sru    }
2082114402Sru  if (first >= 0) {
2083114402Sru    prints('\n');
2084114402Sru    for (i = first + 1; i < ncolumns; i++)
2085114402Sru      if (equal[i])
2086114402Sru	printfs(".nr %1 \\n[%2]\n",
2087114402Sru		span_width_reg(i, i),
2088114402Sru		span_width_reg(first, first));
2089114402Sru  }
2090114402Sru}
2091114402Sru
2092114402Sruvoid table::compute_widths()
2093114402Sru{
2094114402Sru  build_span_list();
2095114402Sru  int i;
2096114402Sru  horizontal_span *p;
2097114402Sru  prints(".nr " SEPARATION_FACTOR_REG " 1n\n");
2098114402Sru  for (i = 0; i < ncolumns; i++) {
2099114402Sru    init_span_reg(i, i);
2100114402Sru    if (!minimum_width[i].empty())
2101114402Sru      printfs(".nr %1 %2\n", span_width_reg(i, i), minimum_width[i]);
2102114402Sru  }
2103114402Sru  for (p = span_list; p; p = p->next)
2104114402Sru    init_span_reg(p->start_col, p->end_col);
2105114402Sru  table_entry *q;
2106114402Sru  for (q = entry_list; q; q = q->next)
2107114402Sru    if (!q->mod->zero_width)
2108114402Sru      q->do_width();
2109114402Sru  for (i = 0; i < ncolumns; i++)
2110114402Sru    compute_span_width(i, i);
2111114402Sru  for (p = span_list; p; p = p->next)
2112114402Sru    compute_span_width(p->start_col, p->end_col);
2113114402Sru  make_columns_equal();
2114114402Sru  // Note that divide_span keeps equal width columns equal.
2115114402Sru  for (p = span_list; p; p = p->next)
2116114402Sru    divide_span(p->start_col, p->end_col);
2117114402Sru  for (p = span_list; p; p = p->next)
2118114402Sru    sum_columns(p->start_col, p->end_col);
2119114402Sru  int had_spanning_block = 0;
2120114402Sru  int had_equal_block = 0;
2121114402Sru  for (q = entry_list; q; q = q->next)
2122114402Sru    if (q->divert(ncolumns, minimum_width,
2123114402Sru		  (flags & EXPAND) ? column_separation : 0)) {
2124114402Sru      if (q->end_col > q->start_col)
2125114402Sru	had_spanning_block = 1;
2126114402Sru      for (i = q->start_col; i <= q->end_col && !had_equal_block; i++)
2127114402Sru	if (equal[i])
2128114402Sru	  had_equal_block = 1;
2129114402Sru    }
2130114402Sru  if (had_equal_block)
2131114402Sru    make_columns_equal();
2132114402Sru  if (had_spanning_block)
2133114402Sru    for (p = span_list; p; p = p->next)
2134114402Sru      divide_span(p->start_col, p->end_col);
2135114402Sru  compute_separation_factor();
2136114402Sru  for (p = span_list; p; p = p->next)
2137114402Sru    sum_columns(p->start_col, p->end_col);
2138114402Sru  compute_column_positions();
2139114402Sru}
2140114402Sru
2141114402Sruvoid table::print_single_hline(int r)
2142114402Sru{
2143114402Sru  prints(".vs " LINE_SEP ">?\\n[.V]u\n"
2144114402Sru	 ".ls 1\n"
2145114402Sru	 "\\v'" BODY_DEPTH "'"
2146114402Sru	 "\\s[\\n[" LINESIZE_REG "]]");
2147114402Sru  if (r > nrows - 1)
2148114402Sru    prints("\\D'l |\\n[TW]u 0'");
2149114402Sru  else {
2150114402Sru    int start_col = 0;
2151114402Sru    for (;;) {
2152114402Sru      while (start_col < ncolumns
2153114402Sru	     && entry[r][start_col] != 0
2154114402Sru	     && entry[r][start_col]->start_row != r)
2155114402Sru	start_col++;
2156114402Sru      int end_col;
2157114402Sru      for (end_col = start_col;
2158114402Sru	   end_col < ncolumns
2159114402Sru	   && (entry[r][end_col] == 0
2160114402Sru	       || entry[r][end_col]->start_row == r);
2161114402Sru	   end_col++)
2162114402Sru	;
2163114402Sru      if (end_col <= start_col)
2164114402Sru	break;
2165114402Sru      printfs("\\h'|\\n[%1]u",
2166114402Sru	      column_divide_reg(start_col));
2167114402Sru      if ((r > 0 && vline[r-1][start_col] == 2)
2168114402Sru	  || (r < nrows && vline[r][start_col] == 2))
2169114402Sru	prints("-" HALF_DOUBLE_LINE_SEP);
2170114402Sru      prints("'");
2171114402Sru      printfs("\\D'l |\\n[%1]u",
2172114402Sru	      column_divide_reg(end_col));
2173114402Sru      if ((r > 0 && vline[r-1][end_col] == 2)
2174114402Sru	  || (r < nrows && vline[r][end_col] == 2))
2175114402Sru	prints("+" HALF_DOUBLE_LINE_SEP);
2176114402Sru      prints(" 0'");
2177114402Sru      start_col = end_col;
2178114402Sru    }
2179114402Sru  }
2180114402Sru  prints("\\s0\n");
2181114402Sru  prints(".ls\n"
2182114402Sru	 ".vs\n");
2183114402Sru}
2184114402Sru
2185114402Sruvoid table::print_double_hline(int r)
2186114402Sru{
2187114402Sru  prints(".vs " LINE_SEP "+" DOUBLE_LINE_SEP
2188114402Sru	 ">?\\n[.V]u\n"
2189114402Sru	 ".ls 1\n"
2190114402Sru	 "\\v'" BODY_DEPTH "'"
2191114402Sru	 "\\s[\\n[" LINESIZE_REG "]]");
2192114402Sru  if (r > nrows - 1)
2193114402Sru    prints("\\v'-" DOUBLE_LINE_SEP "'"
2194114402Sru	   "\\D'l |\\n[TW]u 0'"
2195114402Sru	   "\\v'" DOUBLE_LINE_SEP "'"
2196114402Sru	   "\\h'|0'"
2197114402Sru	   "\\D'l |\\n[TW]u 0'");
2198114402Sru  else {
2199114402Sru    int start_col = 0;
2200114402Sru    for (;;) {
2201114402Sru      while (start_col < ncolumns
2202114402Sru	     && entry[r][start_col] != 0
2203114402Sru	     && entry[r][start_col]->start_row != r)
2204114402Sru	start_col++;
2205114402Sru      int end_col;
2206114402Sru      for (end_col = start_col;
2207114402Sru	   end_col < ncolumns
2208114402Sru	   && (entry[r][end_col] == 0
2209114402Sru	       || entry[r][end_col]->start_row == r);
2210114402Sru	   end_col++)
2211114402Sru	;
2212114402Sru      if (end_col <= start_col)
2213114402Sru	break;
2214114402Sru      const char *left_adjust = 0;
2215114402Sru      if ((r > 0 && vline[r-1][start_col] == 2)
2216114402Sru	  || (r < nrows && vline[r][start_col] == 2))
2217114402Sru	left_adjust = "-" HALF_DOUBLE_LINE_SEP;
2218114402Sru      const char *right_adjust = 0;
2219114402Sru      if ((r > 0 && vline[r-1][end_col] == 2)
2220114402Sru	  || (r < nrows && vline[r][end_col] == 2))
2221114402Sru	right_adjust = "+" HALF_DOUBLE_LINE_SEP;
2222114402Sru      printfs("\\v'-" DOUBLE_LINE_SEP "'"
2223114402Sru	      "\\h'|\\n[%1]u",
2224114402Sru	      column_divide_reg(start_col));
2225114402Sru      if (left_adjust)
2226114402Sru	prints(left_adjust);
2227114402Sru      prints("'");
2228114402Sru      printfs("\\D'l |\\n[%1]u",
2229114402Sru	      column_divide_reg(end_col));
2230114402Sru      if (right_adjust)
2231114402Sru	prints(right_adjust);
2232114402Sru      prints(" 0'");
2233114402Sru      printfs("\\v'" DOUBLE_LINE_SEP "'"
2234114402Sru	      "\\h'|\\n[%1]u",
2235114402Sru	      column_divide_reg(start_col));
2236114402Sru      if (left_adjust)
2237114402Sru	prints(left_adjust);
2238114402Sru      prints("'");
2239114402Sru      printfs("\\D'l |\\n[%1]u",
2240114402Sru	      column_divide_reg(end_col));
2241114402Sru      if (right_adjust)
2242114402Sru	prints(right_adjust);
2243114402Sru      prints(" 0'");
2244114402Sru      start_col = end_col;
2245114402Sru    }
2246114402Sru  }
2247114402Sru  prints("\\s0\n"
2248114402Sru	 ".ls\n"
2249114402Sru	 ".vs\n");
2250114402Sru}
2251114402Sru
2252114402Sruvoid table::compute_vrule_top_adjust(int start_row, int col, string &result)
2253114402Sru{
2254114402Sru  if (row_is_all_lines[start_row] && start_row < nrows - 1) {
2255114402Sru    if (row_is_all_lines[start_row] == 2)
2256114402Sru      result = LINE_SEP ">?\\n[.V]u" "+" DOUBLE_LINE_SEP;
2257114402Sru    else
2258114402Sru      result = LINE_SEP ">?\\n[.V]u";
2259114402Sru    start_row++;
2260114402Sru  }
2261114402Sru  else {
2262114402Sru    result = "";
2263114402Sru    if (start_row == 0)
2264114402Sru      return;
2265114402Sru    for (stuff *p = stuff_list; p && p->row <= start_row; p = p->next)
2266114402Sru      if (p->row == start_row
2267114402Sru	  && (p->is_single_line() || p->is_double_line()))
2268114402Sru	return;
2269114402Sru  }
2270114402Sru  int left = 0;
2271114402Sru  if (col > 0) {
2272114402Sru    table_entry *e = entry[start_row-1][col-1];
2273114402Sru    if (e && e->start_row == e->end_row) {
2274114402Sru      if (e->to_double_line_entry() != 0)
2275114402Sru	left = 2;
2276114402Sru      else if (e->to_single_line_entry() != 0)
2277114402Sru	left = 1;
2278114402Sru    }
2279114402Sru  }
2280114402Sru  int right = 0;
2281114402Sru  if (col < ncolumns) {
2282114402Sru    table_entry *e = entry[start_row-1][col];
2283114402Sru    if (e && e->start_row == e->end_row) {
2284114402Sru      if (e->to_double_line_entry() != 0)
2285114402Sru	right = 2;
2286114402Sru      else if (e->to_single_line_entry() != 0)
2287114402Sru	right = 1;
2288114402Sru    }
2289114402Sru  }
2290114402Sru  if (row_is_all_lines[start_row-1] == 0) {
2291114402Sru    if (left > 0 || right > 0) {
2292114402Sru      result += "-" BODY_DEPTH "-" BAR_HEIGHT;
2293114402Sru      if ((left == 2 && right != 2) || (right == 2 && left != 2))
2294114402Sru	result += "-" HALF_DOUBLE_LINE_SEP;
2295114402Sru      else if (left == 2 && right == 2)
2296114402Sru	result += "+" HALF_DOUBLE_LINE_SEP;
2297114402Sru    }
2298114402Sru  }
2299114402Sru  else if (row_is_all_lines[start_row-1] == 2) {
2300114402Sru    if ((left == 2 && right != 2) || (right == 2 && left != 2))
2301114402Sru      result += "-" DOUBLE_LINE_SEP;
2302114402Sru    else if (left == 1 || right == 1)
2303114402Sru      result += "-" HALF_DOUBLE_LINE_SEP;
2304114402Sru  }
2305114402Sru}
2306114402Sru
2307114402Sruvoid table::compute_vrule_bot_adjust(int end_row, int col, string &result)
2308114402Sru{
2309114402Sru  if (row_is_all_lines[end_row] && end_row > 0) {
2310114402Sru    end_row--;
2311114402Sru    result = "";
2312114402Sru  }
2313114402Sru  else {
2314114402Sru    stuff *p;
2315114402Sru    for (p = stuff_list; p && p->row < end_row + 1; p = p->next)
2316114402Sru      ;
2317114402Sru    if (p && p->row == end_row + 1 && p->is_double_line()) {
2318114402Sru      result = "-" DOUBLE_LINE_SEP;
2319114402Sru      return;
2320114402Sru    }
2321114402Sru    if ((p != 0 && p->row == end_row + 1)
2322114402Sru	|| end_row == nrows - 1) {
2323114402Sru      result = "";
2324114402Sru      return;
2325114402Sru    }
2326114402Sru    if (row_is_all_lines[end_row+1] == 1)
2327114402Sru      result = LINE_SEP;
2328114402Sru    else if (row_is_all_lines[end_row+1] == 2)
2329114402Sru      result = LINE_SEP "+" DOUBLE_LINE_SEP;
2330114402Sru    else
2331114402Sru      result = "";
2332114402Sru  }
2333114402Sru  int left = 0;
2334114402Sru  if (col > 0) {
2335114402Sru    table_entry *e = entry[end_row+1][col-1];
2336114402Sru    if (e && e->start_row == e->end_row) {
2337114402Sru      if (e->to_double_line_entry() != 0)
2338114402Sru	left = 2;
2339114402Sru      else if (e->to_single_line_entry() != 0)
2340114402Sru	left = 1;
2341114402Sru    }
2342114402Sru  }
2343114402Sru  int right = 0;
2344114402Sru  if (col < ncolumns) {
2345114402Sru    table_entry *e = entry[end_row+1][col];
2346114402Sru    if (e && e->start_row == e->end_row) {
2347114402Sru      if (e->to_double_line_entry() != 0)
2348114402Sru	right = 2;
2349114402Sru      else if (e->to_single_line_entry() != 0)
2350114402Sru	right = 1;
2351114402Sru    }
2352114402Sru  }
2353114402Sru  if (row_is_all_lines[end_row+1] == 0) {
2354114402Sru    if (left > 0 || right > 0) {
2355114402Sru      result = "1v-" BODY_DEPTH "-" BAR_HEIGHT;
2356114402Sru      if ((left == 2 && right != 2) || (right == 2 && left != 2))
2357114402Sru	result += "+" HALF_DOUBLE_LINE_SEP;
2358114402Sru      else if (left == 2 && right == 2)
2359114402Sru	result += "-" HALF_DOUBLE_LINE_SEP;
2360114402Sru    }
2361114402Sru  }
2362114402Sru  else if (row_is_all_lines[end_row+1] == 2) {
2363114402Sru    if (left == 2 && right == 2)
2364114402Sru      result += "-" DOUBLE_LINE_SEP;
2365114402Sru    else if (left != 2 && right != 2 && (left == 1 || right == 1))
2366114402Sru      result += "-" HALF_DOUBLE_LINE_SEP;
2367114402Sru  }
2368114402Sru}
2369114402Sru
2370114402Sruvoid table::add_vertical_rule(int start_row, int end_row, int col, int is_double)
2371114402Sru{
2372114402Sru  vrule_list = new vertical_rule(start_row, end_row, col, is_double,
2373114402Sru				 vrule_list);
2374114402Sru  compute_vrule_top_adjust(start_row, col, vrule_list->top_adjust);
2375114402Sru  compute_vrule_bot_adjust(end_row, col, vrule_list->bot_adjust);
2376114402Sru}
2377114402Sru
2378114402Sruvoid table::build_vrule_list()
2379114402Sru{
2380114402Sru  int col;
2381114402Sru  if (flags & ALLBOX) {
2382114402Sru    for (col = 1; col < ncolumns; col++) {
2383114402Sru      int start_row = 0;
2384114402Sru      for (;;) {
2385114402Sru	while (start_row < nrows && vline_spanned(start_row, col))
2386114402Sru	  start_row++;
2387114402Sru	if (start_row >= nrows)
2388114402Sru	  break;
2389114402Sru	int end_row = start_row;
2390114402Sru	while (end_row < nrows && !vline_spanned(end_row, col))
2391114402Sru	  end_row++;
2392114402Sru	end_row--;
2393114402Sru	add_vertical_rule(start_row, end_row, col, 0);
2394114402Sru	start_row = end_row + 1;
2395114402Sru      }
2396114402Sru    }
2397114402Sru  }
2398114402Sru  if (flags & (BOX|ALLBOX|DOUBLEBOX)) {
2399114402Sru    add_vertical_rule(0, nrows - 1, 0, 0);
2400114402Sru    add_vertical_rule(0, nrows - 1, ncolumns, 0);
2401114402Sru  }
2402114402Sru  for (int end_row = 0; end_row < nrows; end_row++)
2403114402Sru    for (col = 0; col < ncolumns+1; col++)
2404114402Sru      if (vline[end_row][col] > 0
2405114402Sru	  && !vline_spanned(end_row, col)
2406114402Sru	  && (end_row == nrows - 1
2407114402Sru	      || vline[end_row+1][col] != vline[end_row][col]
2408114402Sru	      || vline_spanned(end_row+1, col))) {
2409114402Sru	int start_row;
2410114402Sru	for (start_row = end_row - 1;
2411114402Sru	     start_row >= 0
2412114402Sru	     && vline[start_row][col] == vline[end_row][col]
2413114402Sru	     && !vline_spanned(start_row, col);
2414114402Sru	     start_row--)
2415114402Sru	  ;
2416114402Sru	start_row++;
2417114402Sru	add_vertical_rule(start_row, end_row, col, vline[end_row][col] > 1);
2418114402Sru      }
2419114402Sru  for (vertical_rule *p = vrule_list; p; p = p->next)
2420114402Sru    if (p->is_double)
2421114402Sru      for (int r = p->start_row; r <= p->end_row; r++) {
2422114402Sru	if (p->col > 0 && entry[r][p->col-1] != 0
2423114402Sru	    && entry[r][p->col-1]->end_col == p->col-1) {
2424114402Sru	  int is_corner = r == p->start_row || r == p->end_row;
2425114402Sru	  entry[r][p->col-1]->note_double_vrule_on_right(is_corner);
2426114402Sru	}
2427114402Sru	if (p->col < ncolumns && entry[r][p->col] != 0
2428114402Sru	    && entry[r][p->col]->start_col == p->col) {
2429114402Sru	  int is_corner = r == p->start_row || r == p->end_row;
2430114402Sru	  entry[r][p->col]->note_double_vrule_on_left(is_corner);
2431114402Sru	}
2432114402Sru      }
2433114402Sru}
2434114402Sru
2435114402Sruvoid table::define_bottom_macro()
2436114402Sru{
2437114402Sru  prints(".eo\n"
2438114402Sru	 ".de T#\n"
2439114402Sru	 ".if !\\n[" SUPPRESS_BOTTOM_REG "] \\{"
2440114402Sru	 "." REPEATED_VPT_MACRO " 0\n"
2441114402Sru	 ".mk " SAVED_VERTICAL_POS_REG "\n");
2442114402Sru  if (flags & (BOX|ALLBOX|DOUBLEBOX)) {
2443114402Sru    prints(".if \\n[T.]&\\n[" NEED_BOTTOM_RULE_REG "] \\{");
2444114402Sru    print_single_hline(0);
2445114402Sru    prints(".\\}\n");
2446114402Sru  }
2447114402Sru  prints(".ls 1\n");
2448114402Sru  for (vertical_rule *p = vrule_list; p; p = p->next)
2449114402Sru    p->contribute_to_bottom_macro(this);
2450114402Sru  if (flags & DOUBLEBOX)
2451114402Sru    prints(".if \\n[T.] \\{.vs " DOUBLE_LINE_SEP ">?\\n[.V]u\n"
2452114402Sru	   "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]"
2453114402Sru	   "\\D'l \\n[TW]u 0'\\s0\n"
2454114402Sru	   ".vs\n"
2455114402Sru	   ".\\}\n"
2456114402Sru	   ".if \\n[" LAST_PASSED_ROW_REG "]>=0 "
2457114402Sru	   ".nr " TOP_REG " \\n[#T]-" DOUBLE_LINE_SEP "\n"
2458114402Sru	   ".sp -1\n"
2459114402Sru	   "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]"
2460114402Sru	   "\\D'l 0 |\\n[" TOP_REG "]u-1v'\\s0\n"
2461114402Sru	   ".sp -1\n"
2462114402Sru	   "\\v'" BODY_DEPTH "'\\h'|\\n[TW]u'\\s[\\n[" LINESIZE_REG "]]"
2463114402Sru	   "\\D'l 0 |\\n[" TOP_REG "]u-1v'\\s0\n");
2464114402Sru  prints(".ls\n");
2465114402Sru  prints(".nr " LAST_PASSED_ROW_REG " \\n[" CURRENT_ROW_REG "]\n"
2466114402Sru	 ".sp |\\n[" SAVED_VERTICAL_POS_REG "]u\n"
2467114402Sru	 "." REPEATED_VPT_MACRO " 1\n"
2468114402Sru	 ".\\}\n"
2469114402Sru	 "..\n"
2470114402Sru	 ".ec\n");
2471114402Sru}
2472114402Sru
2473114402Sru// is the vertical line before column c in row r horizontally spanned?
2474114402Sru
2475114402Sruint table::vline_spanned(int r, int c)
2476114402Sru{
2477114402Sru  assert(r >= 0 && r < nrows && c >= 0 && c < ncolumns + 1);
2478114402Sru  return (c != 0 && c != ncolumns && entry[r][c] != 0
2479114402Sru	  && entry[r][c]->start_col != c
2480114402Sru	  // horizontally spanning lines don't count
2481114402Sru	  && entry[r][c]->to_double_line_entry() == 0
2482114402Sru	  && entry[r][c]->to_single_line_entry() == 0);
2483114402Sru}
2484114402Sru
2485114402Sruint table::row_begins_section(int r)
2486114402Sru{
2487114402Sru  assert(r >= 0 && r < nrows);
2488114402Sru  for (int i = 0; i < ncolumns; i++)
2489114402Sru    if (entry[r][i] && entry[r][i]->start_row != r)
2490114402Sru      return 0;
2491114402Sru  return 1;
2492114402Sru}
2493114402Sru
2494114402Sruint table::row_ends_section(int r)
2495114402Sru{
2496114402Sru  assert(r >= 0 && r < nrows);
2497114402Sru  for (int i = 0; i < ncolumns; i++)
2498114402Sru    if (entry[r][i] && entry[r][i]->end_row != r)
2499114402Sru      return 0;
2500114402Sru  return 1;
2501114402Sru}
2502114402Sru
2503114402Sruvoid table::do_row(int r)
2504114402Sru{
2505114402Sru  if (!(flags & NOKEEP) && row_begins_section(r))
2506114402Sru    prints("." KEEP_MACRO_NAME "\n");
2507114402Sru  int had_line = 0;
2508114402Sru  stuff *p;
2509114402Sru  for (p = stuff_list; p && p->row < r; p = p->next)
2510114402Sru    ;
2511114402Sru  for (stuff *p1 = p; p1 && p1->row == r; p1 = p1->next)
2512114402Sru    if (!p1->printed && (p1->is_single_line() || p1->is_double_line())) {
2513114402Sru      had_line = 1;
2514114402Sru      break;
2515114402Sru    }
2516114402Sru  if (!had_line && !row_is_all_lines[r])
2517114402Sru    printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r));
2518114402Sru  had_line = 0;
2519114402Sru  for (; p && p->row == r; p = p->next)
2520114402Sru    if (!p->printed) {
2521114402Sru      p->print(this);
2522114402Sru      if (!had_line && (p->is_single_line() || p->is_double_line())) {
2523114402Sru	printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r));
2524114402Sru	had_line = 1;
2525114402Sru      }
2526114402Sru    }
2527114402Sru  // Change the row *after* printing the stuff list (which might contain .TH).
2528114402Sru  printfs("\\*[" TRANSPARENT_STRING_NAME "].nr " CURRENT_ROW_REG " %1\n",
2529114402Sru	  as_string(r));
2530114402Sru  if (!had_line && row_is_all_lines[r])
2531114402Sru    printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r));
2532114402Sru  // we might have had a .TH, for example,  since we last tried
2533114402Sru  if (!(flags & NOKEEP) && row_begins_section(r))
2534114402Sru    prints("." KEEP_MACRO_NAME "\n");
2535114402Sru  printfs(".mk %1\n", row_start_reg(r));
2536114402Sru  prints(".mk " BOTTOM_REG "\n"
2537114402Sru	 "." REPEATED_VPT_MACRO " 0\n");
2538114402Sru  int c;
2539114402Sru  int row_is_blank = 1;
2540114402Sru  int first_start_row = r;
2541114402Sru  for (c = 0; c < ncolumns; c++) {
2542114402Sru    table_entry *e = entry[r][c];
2543114402Sru    if (e) {
2544114402Sru      if (e->end_row == r) {
2545114402Sru	e->do_depth();
2546114402Sru	if (e->start_row < first_start_row)
2547114402Sru	  first_start_row = e->start_row;
2548114402Sru	row_is_blank = 0;
2549114402Sru      }
2550114402Sru      c = e->end_col;
2551114402Sru    }
2552114402Sru  }
2553114402Sru  if (row_is_blank)
2554114402Sru    prints(".nr " BOTTOM_REG " +1v\n");
2555114402Sru  if (row_is_all_lines[r]) {
2556114402Sru    prints(".vs " LINE_SEP);
2557114402Sru    if (row_is_all_lines[r] == 2)
2558114402Sru      prints("+" DOUBLE_LINE_SEP);
2559114402Sru    prints(">?\\n[.V]u\n.ls 1\n");
2560114402Sru    prints("\\&");
2561114402Sru    prints("\\v'" BODY_DEPTH);
2562114402Sru    if (row_is_all_lines[r] == 2)
2563114402Sru      prints("-" HALF_DOUBLE_LINE_SEP);
2564114402Sru    prints("'");
2565114402Sru    for (c = 0; c < ncolumns; c++) {
2566114402Sru      table_entry *e = entry[r][c];
2567114402Sru      if (e) {
2568114402Sru	if (e->end_row == e->start_row)
2569114402Sru	  e->to_simple_entry()->simple_print(1);
2570114402Sru	c = e->end_col;
2571114402Sru      }
2572114402Sru    }
2573114402Sru    prints("\n");
2574114402Sru    prints(".ls\n"
2575114402Sru	   ".vs\n");
2576114402Sru    prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n");
2577114402Sru    printfs(".sp |\\n[%1]u\n", row_start_reg(r));
2578114402Sru  }
2579114402Sru  for (int i = row_is_all_lines[r] ? r - 1 : r;
2580114402Sru       i >= first_start_row;
2581114402Sru       i--) {
2582114402Sru    simple_entry *first = 0;
2583114402Sru    for (c = 0; c < ncolumns; c++) {
2584114402Sru      table_entry *e = entry[r][c];
2585114402Sru      if (e) {
2586114402Sru	if (e->end_row == r && e->start_row == i) {
2587114402Sru	  simple_entry *simple = e->to_simple_entry();
2588114402Sru	  if (simple) {
2589114402Sru	    if (!first) {
2590114402Sru	      prints(".ta");
2591114402Sru	      first = simple;
2592114402Sru	    }
2593114402Sru	    simple->add_tab();
2594114402Sru	  }
2595114402Sru	}
2596114402Sru	c = e->end_col;
2597114402Sru      }
2598114402Sru    }
2599114402Sru    if (first) {
2600114402Sru      prints('\n');
2601114402Sru      first->position_vertically();
2602114402Sru      first->set_location();
2603114402Sru      prints("\\&");
2604114402Sru      first->simple_print(0);
2605114402Sru      for (c = first->end_col + 1; c < ncolumns; c++) {
2606114402Sru	table_entry *e = entry[r][c];
2607114402Sru	if (e) {
2608114402Sru	  if (e->end_row == r && e->start_row == i) {
2609114402Sru	    simple_entry *simple = e->to_simple_entry();
2610151497Sru	    if (simple) {
2611151497Sru	      if (e->end_row != e->start_row) {
2612151497Sru		prints('\n');
2613151497Sru		simple->position_vertically();
2614151497Sru		prints("\\&");
2615151497Sru	      }
2616114402Sru	      simple->simple_print(0);
2617151497Sru	    }
2618114402Sru	  }
2619114402Sru	  c = e->end_col;
2620114402Sru	}
2621114402Sru      }
2622114402Sru      prints('\n');
2623114402Sru      prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n");
2624114402Sru      printfs(".sp |\\n[%1]u\n", row_start_reg(r));
2625114402Sru    }
2626114402Sru  }
2627114402Sru  for (c = 0; c < ncolumns; c++) {
2628114402Sru    table_entry *e = entry[r][c];
2629114402Sru    if (e) {
2630114402Sru      if (e->end_row == r && e->to_simple_entry() == 0) {
2631114402Sru	e->position_vertically();
2632114402Sru	e->print();
2633114402Sru	prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n");
2634114402Sru	printfs(".sp |\\n[%1]u\n", row_start_reg(r));
2635114402Sru      }
2636114402Sru      c = e->end_col;
2637114402Sru    }
2638114402Sru  }
2639114402Sru  prints("." REPEATED_VPT_MACRO " 1\n"
2640114402Sru	 ".sp |\\n[" BOTTOM_REG "]u\n"
2641114402Sru	 "\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG " 1\n");
2642114402Sru  if (r != nrows - 1 && (flags & ALLBOX)) {
2643114402Sru    print_single_hline(r + 1);
2644114402Sru    prints("\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG " 0\n");
2645114402Sru  }
2646114402Sru  if (r != nrows - 1) {
2647114402Sru    if (p && p->row == r + 1
2648114402Sru	&& (p->is_single_line() || p->is_double_line())) {
2649114402Sru      p->print(this);
2650114402Sru      prints("\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG
2651114402Sru	     " 0\n");
2652114402Sru    }
2653114402Sru    int printed_one = 0;
2654114402Sru    for (vertical_rule *vr = vrule_list; vr; vr = vr->next)
2655114402Sru      if (vr->end_row == r) {
2656114402Sru	if (!printed_one) {
2657114402Sru	  prints("." REPEATED_VPT_MACRO " 0\n");
2658114402Sru	  printed_one = 1;
2659114402Sru	}
2660114402Sru	vr->print();
2661114402Sru      }
2662114402Sru    if (printed_one)
2663114402Sru      prints("." REPEATED_VPT_MACRO " 1\n");
2664114402Sru    if (!(flags & NOKEEP) && row_ends_section(r))
2665114402Sru      prints("." RELEASE_MACRO_NAME "\n");
2666114402Sru  }
2667114402Sru}
2668114402Sru
2669114402Sruvoid table::do_top()
2670114402Sru{
2671114402Sru  prints(".fc \002\003\n");
2672114402Sru  if (!(flags & NOKEEP) && (flags & (BOX|DOUBLEBOX|ALLBOX)))
2673114402Sru    prints("." TABLE_KEEP_MACRO_NAME "\n");
2674114402Sru  if (flags & DOUBLEBOX) {
2675114402Sru    prints(".ls 1\n"
2676114402Sru	   ".vs " LINE_SEP ">?\\n[.V]u\n"
2677114402Sru	   "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]\\D'l \\n[TW]u 0'\\s0\n"
2678114402Sru	   ".vs\n"
2679114402Sru	   "." REPEATED_MARK_MACRO " " TOP_REG "\n"
2680114402Sru	   ".vs " DOUBLE_LINE_SEP ">?\\n[.V]u\n");
2681114402Sru    printfs("\\v'" BODY_DEPTH "'"
2682114402Sru	    "\\s[\\n[" LINESIZE_REG "]]"
2683114402Sru	    "\\h'\\n[%1]u'"
2684114402Sru	    "\\D'l |\\n[%2]u 0'"
2685114402Sru	    "\\s0"
2686114402Sru	    "\n",
2687114402Sru	    column_divide_reg(0),
2688114402Sru	    column_divide_reg(ncolumns));
2689114402Sru    prints(".ls\n"
2690114402Sru	   ".vs\n");
2691114402Sru  }
2692114402Sru  else if (flags & (ALLBOX|BOX)) {
2693114402Sru    print_single_hline(0);
2694114402Sru  }
2695114402Sru  //printfs(".mk %1\n", row_top_reg(0));
2696114402Sru}
2697114402Sru
2698114402Sruvoid table::do_bottom()
2699114402Sru{
2700114402Sru  // print stuff after last row
2701114402Sru  for (stuff *p = stuff_list; p; p = p->next)
2702114402Sru    if (p->row > nrows - 1)
2703114402Sru      p->print(this);
2704114402Sru  if (!(flags & NOKEEP))
2705114402Sru    prints("." RELEASE_MACRO_NAME "\n");
2706114402Sru  printfs(".mk %1\n", row_top_reg(nrows));
2707114402Sru  prints(".nr " NEED_BOTTOM_RULE_REG " 1\n"
2708114402Sru	 ".nr T. 1\n"
2709114402Sru	 ".T#\n");
2710114402Sru  if (!(flags & NOKEEP) && (flags & (BOX|DOUBLEBOX|ALLBOX)))
2711114402Sru    prints("." TABLE_RELEASE_MACRO_NAME "\n");
2712114402Sru  if (flags & DOUBLEBOX)
2713114402Sru    prints(".sp " DOUBLE_LINE_SEP "\n");
2714114402Sru  prints("." RESET_MACRO_NAME "\n"
2715114402Sru	 ".fc\n"
2716114402Sru	 ".cp \\n(" COMPATIBLE_REG "\n");
2717114402Sru}
2718114402Sru
2719114402Sruint table::get_nrows()
2720114402Sru{
2721114402Sru  return nrows;
2722114402Sru}
2723114402Sru
2724114402Sruconst char *last_filename = 0;
2725114402Sru
2726114402Sruvoid set_troff_location(const char *fn, int ln)
2727114402Sru{
2728114402Sru  if (!location_force_filename && last_filename != 0
2729114402Sru      && strcmp(fn, last_filename) == 0)
2730114402Sru    printfs(".lf %1\n", as_string(ln));
2731114402Sru  else {
2732114402Sru    printfs(".lf %1 %2\n", as_string(ln), fn);
2733114402Sru    last_filename = fn;
2734114402Sru    location_force_filename = 0;
2735114402Sru  }
2736114402Sru}
2737114402Sru
2738114402Sruvoid printfs(const char *s, const string &arg1, const string &arg2,
2739114402Sru	     const string &arg3, const string &arg4, const string &arg5)
2740114402Sru{
2741114402Sru  if (s) {
2742114402Sru    char c;
2743114402Sru    while ((c = *s++) != '\0') {
2744114402Sru      if (c == '%') {
2745114402Sru	switch (*s++) {
2746114402Sru	case '1':
2747114402Sru	  prints(arg1);
2748114402Sru	  break;
2749114402Sru	case '2':
2750114402Sru	  prints(arg2);
2751114402Sru	  break;
2752114402Sru	case '3':
2753114402Sru	  prints(arg3);
2754114402Sru	  break;
2755114402Sru	case '4':
2756114402Sru	  prints(arg4);
2757114402Sru	  break;
2758114402Sru	case '5':
2759114402Sru	  prints(arg5);
2760114402Sru	  break;
2761114402Sru	case '6':
2762114402Sru	case '7':
2763114402Sru	case '8':
2764114402Sru	case '9':
2765114402Sru	  break;
2766114402Sru	case '%':
2767114402Sru	  prints('%');
2768114402Sru	  break;
2769114402Sru	default:
2770114402Sru	  assert(0);
2771114402Sru	}
2772114402Sru      }
2773114402Sru      else
2774114402Sru	prints(c);
2775114402Sru    }
2776114402Sru  }
2777114402Sru}
2778114402Sru
2779