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