home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / usr.bin / groff / tbl / table.cc < prev    next >
Encoding:
C/C++ Source or Header  |  1991-04-30  |  67.7 KB  |  2,734 lines

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