LCOV - code coverage report
Current view: top level - preproc/tbl - table.cpp (source / functions) Hit Total Coverage
Test: GNU roff Lines: 1499 1832 81.8 %
Date: 2026-01-16 17:51:41 Functions: 144 166 86.7 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14