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