PageRenderTime 183ms CodeModel.GetById 23ms app.highlight 146ms RepoModel.GetById 1ms app.codeStats 1ms

/contrib/groff/src/preproc/tbl/table.cpp

https://bitbucket.org/freebsd/freebsd-head/
C++ | 2778 lines | 2508 code | 206 blank | 64 comment | 689 complexity | 1207b28aa9e2b714dd0420b955f47f93 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1// -*- C++ -*-
   2/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2003, 2004
   3   Free Software Foundation, Inc.
   4     Written by James Clark (jjc@jclark.com)
   5
   6This file is part of groff.
   7
   8groff is free software; you can redistribute it and/or modify it under
   9the terms of the GNU General Public License as published by the Free
  10Software Foundation; either version 2, or (at your option) any later
  11version.
  12
  13groff is distributed in the hope that it will be useful, but WITHOUT ANY
  14WARRANTY; without even the implied warranty of MERCHANTABILITY or
  15FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  16for more details.
  17
  18You should have received a copy of the GNU General Public License along
  19with groff; see the file COPYING.  If not, write to the Free Software
  20Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
  21
  22#include "table.h"
  23
  24#define BAR_HEIGHT ".25m"
  25#define DOUBLE_LINE_SEP "2p"
  26#define HALF_DOUBLE_LINE_SEP "1p"
  27#define LINE_SEP "2p"
  28#define BODY_DEPTH ".25m"
  29
  30const int DEFAULT_COLUMN_SEPARATION = 3;
  31
  32#define DELIMITER_CHAR "\\[tbl]"
  33#define SEPARATION_FACTOR_REG PREFIX "sep"
  34#define BOTTOM_REG PREFIX "bot"
  35#define RESET_MACRO_NAME PREFIX "init"
  36#define LINESIZE_REG PREFIX "lps"
  37#define TOP_REG PREFIX "top"
  38#define CURRENT_ROW_REG PREFIX "crow"
  39#define LAST_PASSED_ROW_REG PREFIX "passed"
  40#define TRANSPARENT_STRING_NAME PREFIX "trans"
  41#define QUOTE_STRING_NAME PREFIX "quote"
  42#define SECTION_DIVERSION_NAME PREFIX "section"
  43#define SECTION_DIVERSION_FLAG_REG PREFIX "sflag"
  44#define SAVED_VERTICAL_POS_REG PREFIX "vert"
  45#define NEED_BOTTOM_RULE_REG PREFIX "brule"
  46#define KEEP_MACRO_NAME PREFIX "keep"
  47#define RELEASE_MACRO_NAME PREFIX "release"
  48#define SAVED_FONT_REG PREFIX "fnt"
  49#define SAVED_SIZE_REG PREFIX "sz"
  50#define SAVED_FILL_REG PREFIX "fll"
  51#define SAVED_INDENT_REG PREFIX "ind"
  52#define SAVED_CENTER_REG PREFIX "cent"
  53#define TABLE_DIVERSION_NAME PREFIX "table"
  54#define TABLE_DIVERSION_FLAG_REG PREFIX "tflag"
  55#define TABLE_KEEP_MACRO_NAME PREFIX "tkeep"
  56#define TABLE_RELEASE_MACRO_NAME PREFIX "trelease"
  57#define NEEDED_REG PREFIX "needed"
  58#define REPEATED_MARK_MACRO PREFIX "rmk"
  59#define REPEATED_VPT_MACRO PREFIX "rvpt"
  60#define SUPPRESS_BOTTOM_REG PREFIX "supbot"
  61#define SAVED_DN_REG PREFIX "dn"
  62
  63// this must be one character
  64#define COMPATIBLE_REG PREFIX "c"
  65
  66#define LEADER_REG PREFIX LEADER
  67
  68#define BLOCK_WIDTH_PREFIX PREFIX "tbw"
  69#define BLOCK_DIVERSION_PREFIX PREFIX "tbd"
  70#define BLOCK_HEIGHT_PREFIX PREFIX "tbh"
  71#define SPAN_WIDTH_PREFIX PREFIX "w"
  72#define SPAN_LEFT_NUMERIC_WIDTH_PREFIX PREFIX "lnw"
  73#define SPAN_RIGHT_NUMERIC_WIDTH_PREFIX PREFIX "rnw"
  74#define SPAN_ALPHABETIC_WIDTH_PREFIX PREFIX "aw"
  75#define COLUMN_SEPARATION_PREFIX PREFIX "cs"
  76#define ROW_START_PREFIX PREFIX "rs"
  77#define COLUMN_START_PREFIX PREFIX "cl"
  78#define COLUMN_END_PREFIX PREFIX "ce"
  79#define COLUMN_DIVIDE_PREFIX PREFIX "cd"
  80#define ROW_TOP_PREFIX PREFIX "rt"
  81
  82string block_width_reg(int r, int c);
  83string block_diversion_name(int r, int c);
  84string block_height_reg(int r, int c);
  85string span_width_reg(int start_col, int end_col);
  86string span_left_numeric_width_reg(int start_col, int end_col);
  87string span_right_numeric_width_reg(int start_col, int end_col);
  88string span_alphabetic_width_reg(int start_col, int end_col);
  89string column_separation_reg(int col);
  90string row_start_reg(int r);
  91string column_start_reg(int c);
  92string column_end_reg(int c);
  93string column_divide_reg(int c);
  94string row_top_reg(int r);
  95
  96void set_inline_modifier(const entry_modifier *);
  97void restore_inline_modifier(const entry_modifier *m);
  98void set_modifier(const entry_modifier *);
  99int find_decimal_point(const char *s, char decimal_point_char,
 100		       const char *delim);
 101
 102string an_empty_string;
 103int location_force_filename = 0;
 104
 105void printfs(const char *,
 106	     const string &arg1 = an_empty_string,
 107	     const string &arg2 = an_empty_string,
 108	     const string &arg3 = an_empty_string,
 109	     const string &arg4 = an_empty_string,
 110	     const string &arg5 = an_empty_string);
 111
 112void prints(const string &);
 113
 114inline void prints(char c)
 115{
 116  putchar(c);
 117}
 118
 119inline void prints(const char *s)
 120{
 121  fputs(s, stdout);
 122}
 123
 124void prints(const string &s)
 125{
 126  if (!s.empty())
 127    fwrite(s.contents(), 1, s.length(), stdout);
 128}
 129
 130struct horizontal_span {
 131  horizontal_span *next;
 132  int start_col;
 133  int end_col;
 134  horizontal_span(int, int, horizontal_span *);
 135};
 136
 137class single_line_entry;
 138class double_line_entry;
 139class simple_entry;
 140
 141class table_entry {
 142friend class table;
 143  table_entry *next;
 144  int input_lineno;
 145  const char *input_filename;
 146protected:
 147  int start_row;
 148  int end_row;
 149  int start_col;
 150  int end_col;
 151  const entry_modifier *mod;
 152public:
 153  void set_location();
 154  table_entry(const entry_modifier *);
 155  virtual ~table_entry();
 156  virtual int divert(int ncols, const string *mw, int *sep);
 157  virtual void do_width();
 158  virtual void do_depth();
 159  virtual void print() = 0;
 160  virtual void position_vertically() = 0;
 161  virtual single_line_entry *to_single_line_entry();
 162  virtual double_line_entry *to_double_line_entry();
 163  virtual simple_entry *to_simple_entry();
 164  virtual int line_type();
 165  virtual void note_double_vrule_on_right(int);
 166  virtual void note_double_vrule_on_left(int);
 167};
 168
 169class simple_entry : public table_entry {
 170public:
 171  simple_entry(const entry_modifier *);
 172  void print();
 173  void position_vertically();
 174  simple_entry *to_simple_entry();
 175  virtual void add_tab();
 176  virtual void simple_print(int);
 177};
 178
 179class empty_entry : public simple_entry {
 180public:
 181  empty_entry(const entry_modifier *);
 182  int line_type();
 183};
 184
 185class text_entry : public simple_entry {
 186protected:
 187  char *contents;
 188  void print_contents();
 189public:
 190  text_entry(char *, const entry_modifier *);
 191  ~text_entry();
 192};
 193
 194void text_entry::print_contents()
 195{
 196  set_inline_modifier(mod);
 197  prints(contents);
 198  restore_inline_modifier(mod);
 199}
 200
 201class repeated_char_entry : public text_entry {
 202public:
 203  repeated_char_entry(char *s, const entry_modifier *m);
 204  void simple_print(int);
 205};
 206
 207class simple_text_entry : public text_entry {
 208public:
 209  simple_text_entry(char *s, const entry_modifier *m);
 210  void do_width();
 211};
 212
 213class left_text_entry : public simple_text_entry {
 214public:
 215  left_text_entry(char *s, const entry_modifier *m);
 216  void simple_print(int);
 217  void add_tab();
 218};
 219
 220class right_text_entry : public simple_text_entry {
 221public:
 222  right_text_entry(char *s, const entry_modifier *m);
 223  void simple_print(int);
 224  void add_tab();
 225};
 226
 227class center_text_entry : public simple_text_entry {
 228public:
 229  center_text_entry(char *s, const entry_modifier *m);
 230  void simple_print(int);
 231  void add_tab();
 232};
 233
 234class numeric_text_entry : public text_entry {
 235  int dot_pos;
 236public:
 237  numeric_text_entry(char *s, const entry_modifier *m, int pos);
 238  void do_width();
 239  void simple_print(int);
 240};
 241
 242class alphabetic_text_entry : public text_entry {
 243public:
 244  alphabetic_text_entry(char *s, const entry_modifier *m);
 245  void do_width();
 246  void simple_print(int);
 247  void add_tab();
 248};
 249
 250class line_entry : public simple_entry {
 251protected:
 252  char double_vrule_on_right;
 253  char double_vrule_on_left;
 254public:
 255  line_entry(const entry_modifier *);
 256  void note_double_vrule_on_right(int);
 257  void note_double_vrule_on_left(int);
 258  void simple_print(int) = 0;
 259};
 260
 261class single_line_entry : public line_entry {
 262public:
 263  single_line_entry(const entry_modifier *m);
 264  void simple_print(int);
 265  single_line_entry *to_single_line_entry();
 266  int line_type();
 267};
 268
 269class double_line_entry : public line_entry {
 270public:
 271  double_line_entry(const entry_modifier *m);
 272  void simple_print(int);
 273  double_line_entry *to_double_line_entry();
 274  int line_type();
 275};
 276
 277class short_line_entry : public simple_entry {
 278public:
 279  short_line_entry(const entry_modifier *m);
 280  void simple_print(int);
 281  int line_type();
 282};
 283
 284class short_double_line_entry : public simple_entry {
 285public:
 286  short_double_line_entry(const entry_modifier *m);
 287  void simple_print(int);
 288  int line_type();
 289};
 290
 291class block_entry : public table_entry {
 292  char *contents;
 293protected:
 294  void do_divert(int alphabetic, int ncols, const string *mw, int *sep);
 295public:
 296  block_entry(char *s, const entry_modifier *m);
 297  ~block_entry();
 298  int divert(int ncols, const string *mw, int *sep);
 299  void do_width();
 300  void do_depth();
 301  void position_vertically();
 302  void print() = 0;
 303};
 304
 305class left_block_entry : public block_entry {
 306public:
 307  left_block_entry(char *s, const entry_modifier *m);
 308  void print();
 309};
 310
 311class right_block_entry : public block_entry {
 312public:
 313  right_block_entry(char *s, const entry_modifier *m);
 314  void print();
 315};
 316
 317class center_block_entry : public block_entry {
 318public:
 319  center_block_entry(char *s, const entry_modifier *m);
 320  void print();
 321};
 322
 323class alphabetic_block_entry : public block_entry {
 324public:
 325  alphabetic_block_entry(char *s, const entry_modifier *m);
 326  void print();
 327  int divert(int ncols, const string *mw, int *sep);
 328};
 329
 330table_entry::table_entry(const entry_modifier *m)
 331: next(0), input_lineno(-1), input_filename(0),
 332  start_row(-1), end_row(-1), start_col(-1), end_col(-1), mod(m)
 333{
 334}
 335
 336table_entry::~table_entry()
 337{
 338}
 339
 340int table_entry::divert(int, const string *, int *)
 341{
 342  return 0;
 343}
 344
 345void table_entry::do_width()
 346{
 347}
 348
 349single_line_entry *table_entry::to_single_line_entry()
 350{
 351  return 0;
 352}
 353
 354double_line_entry *table_entry::to_double_line_entry()
 355{
 356  return 0;
 357}
 358
 359simple_entry *table_entry::to_simple_entry()
 360{
 361  return 0;
 362}
 363
 364void table_entry::do_depth()
 365{
 366}
 367
 368void table_entry::set_location()
 369{
 370  set_troff_location(input_filename, input_lineno);
 371}
 372
 373int table_entry::line_type()
 374{
 375  return -1;
 376}
 377
 378void table_entry::note_double_vrule_on_right(int)
 379{
 380}
 381
 382void table_entry::note_double_vrule_on_left(int)
 383{
 384}
 385
 386simple_entry::simple_entry(const entry_modifier *m) : table_entry(m)
 387{
 388}
 389
 390void simple_entry::add_tab()
 391{
 392  // do nothing
 393}
 394
 395void simple_entry::simple_print(int)
 396{
 397  // do nothing
 398}
 399
 400void simple_entry::position_vertically()
 401{
 402  if (start_row != end_row)
 403    switch (mod->vertical_alignment) {
 404    case entry_modifier::TOP:
 405      printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
 406      break;
 407    case entry_modifier::CENTER:
 408      // Peform the motion in two stages so that the center is rounded
 409      // vertically upwards even if net vertical motion is upwards.
 410      printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
 411      printfs(".sp \\n[" BOTTOM_REG "]u-\\n[%1]u-1v/2u\n", 
 412	      row_start_reg(start_row));
 413      break;
 414    case entry_modifier::BOTTOM:
 415      printfs(".sp |\\n[%1]u+\\n[" BOTTOM_REG "]u-\\n[%1]u-1v\n", 
 416	      row_start_reg(start_row));
 417      break;
 418    default:
 419      assert(0);
 420    }
 421}
 422
 423void simple_entry::print()
 424{
 425  prints(".ta");
 426  add_tab();
 427  prints('\n');
 428  set_location();
 429  prints("\\&");
 430  simple_print(0);
 431  prints('\n');
 432}
 433
 434simple_entry *simple_entry::to_simple_entry()
 435{
 436  return this;
 437}
 438
 439empty_entry::empty_entry(const entry_modifier *m)
 440: simple_entry(m)
 441{
 442}
 443
 444int empty_entry::line_type()
 445{
 446  return 0;
 447}
 448
 449text_entry::text_entry(char *s, const entry_modifier *m)
 450: simple_entry(m), contents(s)
 451{
 452}
 453
 454text_entry::~text_entry()
 455{
 456  a_delete contents;
 457}
 458
 459repeated_char_entry::repeated_char_entry(char *s, const entry_modifier *m)
 460: text_entry(s, m)
 461{
 462}
 463
 464void repeated_char_entry::simple_print(int)
 465{
 466  printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
 467  set_inline_modifier(mod);
 468  printfs("\\l" DELIMITER_CHAR "\\n[%1]u\\&",
 469	  span_width_reg(start_col, end_col));
 470  prints(contents);
 471  prints(DELIMITER_CHAR);
 472  restore_inline_modifier(mod);
 473}
 474
 475simple_text_entry::simple_text_entry(char *s, const entry_modifier *m)
 476: text_entry(s, m)
 477{
 478}
 479
 480void simple_text_entry::do_width()
 481{
 482  set_location();
 483  printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR,
 484	  span_width_reg(start_col, end_col));
 485  print_contents();
 486  prints(DELIMITER_CHAR "\n");
 487}
 488
 489left_text_entry::left_text_entry(char *s, const entry_modifier *m)
 490: simple_text_entry(s, m)
 491{
 492}
 493
 494void left_text_entry::simple_print(int)
 495{
 496  printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
 497  print_contents();
 498}
 499
 500// The only point of this is to make `\a' ``work'' as in Unix tbl.  Grrr.
 501
 502void left_text_entry::add_tab()
 503{
 504  printfs(" \\n[%1]u", column_end_reg(end_col));
 505}
 506
 507right_text_entry::right_text_entry(char *s, const entry_modifier *m)
 508: simple_text_entry(s, m)
 509{
 510}
 511
 512void right_text_entry::simple_print(int)
 513{
 514  printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
 515  prints("\002\003");
 516  print_contents();
 517  prints("\002");
 518}
 519
 520void right_text_entry::add_tab()
 521{
 522  printfs(" \\n[%1]u", column_end_reg(end_col));
 523}
 524
 525center_text_entry::center_text_entry(char *s, const entry_modifier *m)
 526: simple_text_entry(s, m)
 527{
 528}
 529
 530void center_text_entry::simple_print(int)
 531{
 532  printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
 533  prints("\002\003");
 534  print_contents();
 535  prints("\003\002");
 536}
 537
 538void center_text_entry::add_tab()
 539{
 540  printfs(" \\n[%1]u", column_end_reg(end_col));
 541}
 542
 543numeric_text_entry::numeric_text_entry(char *s, const entry_modifier *m, int pos)
 544: text_entry(s, m), dot_pos(pos)
 545{
 546}
 547
 548void numeric_text_entry::do_width()
 549{
 550  if (dot_pos != 0) {
 551    set_location();
 552    printfs(".nr %1 0\\w" DELIMITER_CHAR,
 553	    block_width_reg(start_row, start_col));
 554    set_inline_modifier(mod);
 555    for (int i = 0; i < dot_pos; i++)
 556      prints(contents[i]);
 557    restore_inline_modifier(mod);
 558    prints(DELIMITER_CHAR "\n");
 559    printfs(".nr %1 \\n[%1]>?\\n[%2]\n",
 560	    span_left_numeric_width_reg(start_col, end_col),
 561	    block_width_reg(start_row, start_col));
 562  }
 563  else
 564    printfs(".nr %1 0\n", block_width_reg(start_row, start_col));
 565  if (contents[dot_pos] != '\0') {
 566    set_location();
 567    printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR,
 568	    span_right_numeric_width_reg(start_col, end_col));
 569    set_inline_modifier(mod);
 570    prints(contents + dot_pos);
 571    restore_inline_modifier(mod);
 572    prints(DELIMITER_CHAR "\n");
 573  }
 574}
 575
 576void numeric_text_entry::simple_print(int)
 577{
 578  printfs("\\h'|(\\n[%1]u-\\n[%2]u-\\n[%3]u/2u+\\n[%2]u+\\n[%4]u-\\n[%5]u)'",
 579	  span_width_reg(start_col, end_col),
 580	  span_left_numeric_width_reg(start_col, end_col),
 581	  span_right_numeric_width_reg(start_col, end_col),
 582	  column_start_reg(start_col),
 583	  block_width_reg(start_row, start_col));
 584  print_contents();
 585}
 586
 587alphabetic_text_entry::alphabetic_text_entry(char *s, const entry_modifier *m)
 588: text_entry(s, m)
 589{
 590}
 591
 592void alphabetic_text_entry::do_width()
 593{
 594  set_location();
 595  printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR,
 596	  span_alphabetic_width_reg(start_col, end_col));
 597  print_contents();
 598  prints(DELIMITER_CHAR "\n");
 599}
 600
 601void alphabetic_text_entry::simple_print(int)
 602{
 603  printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
 604  printfs("\\h'\\n[%1]u-\\n[%2]u/2u'",
 605	  span_width_reg(start_col, end_col),
 606	  span_alphabetic_width_reg(start_col, end_col));
 607  print_contents();
 608}
 609
 610// The only point of this is to make `\a' ``work'' as in Unix tbl.  Grrr.
 611
 612void alphabetic_text_entry::add_tab()
 613{
 614  printfs(" \\n[%1]u", column_end_reg(end_col));
 615}
 616
 617block_entry::block_entry(char *s, const entry_modifier *m)
 618: table_entry(m), contents(s)
 619{
 620}
 621
 622block_entry::~block_entry()
 623{
 624  a_delete contents;
 625}
 626
 627void block_entry::position_vertically()
 628{
 629  if (start_row != end_row)
 630    switch(mod->vertical_alignment) {
 631    case entry_modifier::TOP:
 632      printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
 633      break;
 634    case entry_modifier::CENTER:
 635      // Peform the motion in two stages so that the center is rounded
 636      // vertically upwards even if net vertical motion is upwards.
 637      printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
 638      printfs(".sp \\n[" BOTTOM_REG "]u-\\n[%1]u-\\n[%2]u/2u\n", 
 639	      row_start_reg(start_row),
 640	      block_height_reg(start_row, start_col));
 641      break;
 642    case entry_modifier::BOTTOM:
 643      printfs(".sp |\\n[%1]u+\\n[" BOTTOM_REG "]u-\\n[%1]u-\\n[%2]u\n", 
 644	      row_start_reg(start_row),
 645	      block_height_reg(start_row, start_col));
 646      break;
 647    default:
 648      assert(0);
 649    }
 650  if (mod->stagger)
 651    prints(".sp -.5v\n");
 652}
 653
 654int block_entry::divert(int ncols, const string *mw, int *sep)
 655{
 656  do_divert(0, ncols, mw, sep);
 657  return 1;
 658}
 659
 660void block_entry::do_divert(int alphabetic, int ncols, const string *mw,
 661			    int *sep)
 662{
 663  printfs(".di %1\n", block_diversion_name(start_row, start_col));
 664  prints(".if \\n[" SAVED_FILL_REG "] .fi\n"
 665	 ".in 0\n");
 666  prints(".ll ");
 667  int i;
 668  for (i = start_col; i <= end_col; i++)
 669    if (mw[i].empty())
 670      break;
 671  if (i > end_col) {
 672    // Every column spanned by this entry has a minimum width.
 673    for (int j = start_col; j <= end_col; j++) {
 674      if (j > start_col) {
 675	if (sep)
 676	  printfs("+%1n", as_string(sep[j - 1]));
 677	prints('+');
 678      }
 679      printfs("(n;%1)", mw[j]);
 680    }
 681    printfs(">?\\n[%1]u", span_width_reg(start_col, end_col));
 682  }
 683  else
 684    printfs("(u;\\n[%1]>?(\\n[.l]*%2/%3))", 
 685	    span_width_reg(start_col, end_col), 
 686	    as_string(end_col - start_col + 1),
 687	    as_string(ncols + 1));
 688  if (alphabetic)
 689    prints("-2n");
 690  prints("\n");
 691  prints(".cp \\n(" COMPATIBLE_REG "\n");
 692  set_modifier(mod);
 693  set_location();
 694  prints(contents);
 695  prints(".br\n.di\n.cp 0\n");
 696  if (!mod->zero_width) {
 697    if (alphabetic) {
 698      printfs(".nr %1 \\n[%1]>?(\\n[dl]+2n)\n",
 699	      span_width_reg(start_col, end_col));
 700      printfs(".nr %1 \\n[%1]>?\\n[dl]\n",
 701	      span_alphabetic_width_reg(start_col, end_col));
 702    }
 703    else
 704      printfs(".nr %1 \\n[%1]>?\\n[dl]\n", span_width_reg(start_col, end_col));
 705  }
 706  printfs(".nr %1 \\n[dn]\n", block_height_reg(start_row, start_col));
 707  printfs(".nr %1 \\n[dl]\n", block_width_reg(start_row, start_col));
 708  prints("." RESET_MACRO_NAME "\n"
 709	 ".in \\n[" SAVED_INDENT_REG "]u\n"
 710	 ".nf\n");
 711  // the block might have contained .lf commands
 712  location_force_filename = 1;
 713}
 714
 715void block_entry::do_width()
 716{
 717  // do nothing; the action happens in divert
 718}
 719
 720void block_entry::do_depth()
 721{
 722  printfs(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?(\\n[%1]+\\n[%2])\n",
 723	  row_start_reg(start_row),
 724	  block_height_reg(start_row, start_col));
 725}
 726
 727left_block_entry::left_block_entry(char *s, const entry_modifier *m)
 728: block_entry(s, m)
 729{
 730}
 731
 732void left_block_entry::print()
 733{
 734  printfs(".in +\\n[%1]u\n", column_start_reg(start_col));
 735  printfs(".%1\n", block_diversion_name(start_row, start_col));
 736  prints(".in\n");
 737}
 738
 739right_block_entry::right_block_entry(char *s, const entry_modifier *m)
 740: block_entry(s, m)
 741{
 742}
 743
 744void right_block_entry::print()
 745{
 746  printfs(".in +\\n[%1]u+\\n[%2]u-\\n[%3]u\n",
 747	  column_start_reg(start_col),
 748	  span_width_reg(start_col, end_col),
 749	  block_width_reg(start_row, start_col));
 750  printfs(".%1\n", block_diversion_name(start_row, start_col));
 751  prints(".in\n");
 752}
 753
 754center_block_entry::center_block_entry(char *s, const entry_modifier *m)
 755: block_entry(s, m)
 756{
 757}
 758
 759void center_block_entry::print()
 760{
 761  printfs(".in +\\n[%1]u+(\\n[%2]u-\\n[%3]u/2u)\n",
 762	  column_start_reg(start_col),
 763	  span_width_reg(start_col, end_col),
 764	  block_width_reg(start_row, start_col));
 765  printfs(".%1\n", block_diversion_name(start_row, start_col));
 766  prints(".in\n");
 767}
 768
 769alphabetic_block_entry::alphabetic_block_entry(char *s,
 770					       const entry_modifier *m)
 771: block_entry(s, m)
 772{
 773}
 774
 775int alphabetic_block_entry::divert(int ncols, const string *mw, int *sep)
 776{
 777  do_divert(1, ncols, mw, sep);
 778  return 1;
 779}
 780
 781void alphabetic_block_entry::print()
 782{
 783  printfs(".in +\\n[%1]u+(\\n[%2]u-\\n[%3]u/2u)\n",
 784	  column_start_reg(start_col),
 785	  span_width_reg(start_col, end_col),
 786	  span_alphabetic_width_reg(start_col, end_col));
 787  printfs(".%1\n", block_diversion_name(start_row, start_col));
 788  prints(".in\n");
 789}
 790
 791line_entry::line_entry(const entry_modifier *m)
 792: simple_entry(m), double_vrule_on_right(0), double_vrule_on_left(0)
 793{
 794}
 795
 796void line_entry::note_double_vrule_on_right(int is_corner)
 797{
 798  double_vrule_on_right = is_corner ? 1 : 2;
 799}
 800
 801void line_entry::note_double_vrule_on_left(int is_corner)
 802{
 803  double_vrule_on_left = is_corner ? 1 : 2;
 804}
 805
 806single_line_entry::single_line_entry(const entry_modifier *m)
 807: line_entry(m)
 808{
 809}
 810
 811int single_line_entry::line_type()
 812{
 813  return 1;
 814}
 815
 816void single_line_entry::simple_print(int dont_move)
 817{
 818  printfs("\\h'|\\n[%1]u",
 819	  column_divide_reg(start_col));
 820  if (double_vrule_on_left) {
 821    prints(double_vrule_on_left == 1 ? "-" : "+");
 822    prints(HALF_DOUBLE_LINE_SEP);
 823  }
 824  prints("'");
 825  if (!dont_move)
 826    prints("\\v'-" BAR_HEIGHT "'");
 827  printfs("\\s[\\n[" LINESIZE_REG "]]" "\\D'l |\\n[%1]u",
 828	  column_divide_reg(end_col+1));
 829  if (double_vrule_on_right) {
 830    prints(double_vrule_on_left == 1 ? "+" : "-");
 831    prints(HALF_DOUBLE_LINE_SEP);
 832  }
 833  prints("0'\\s0");
 834  if (!dont_move)
 835    prints("\\v'" BAR_HEIGHT "'");
 836}
 837  
 838single_line_entry *single_line_entry::to_single_line_entry()
 839{
 840  return this;
 841}
 842
 843double_line_entry::double_line_entry(const entry_modifier *m)
 844: line_entry(m)
 845{
 846}
 847
 848int double_line_entry::line_type()
 849{
 850  return 2;
 851}
 852
 853void double_line_entry::simple_print(int dont_move)
 854{
 855  if (!dont_move)
 856    prints("\\v'-" BAR_HEIGHT "'");
 857  printfs("\\h'|\\n[%1]u",
 858	  column_divide_reg(start_col));
 859  if (double_vrule_on_left) {
 860    prints(double_vrule_on_left == 1 ? "-" : "+");
 861    prints(HALF_DOUBLE_LINE_SEP);
 862  }
 863  prints("'");
 864  printfs("\\v'-" HALF_DOUBLE_LINE_SEP "'"
 865	  "\\s[\\n[" LINESIZE_REG "]]"
 866	  "\\D'l |\\n[%1]u",
 867	  column_divide_reg(end_col+1));
 868  if (double_vrule_on_right)
 869    prints("-" HALF_DOUBLE_LINE_SEP);
 870  prints(" 0'");
 871  printfs("\\v'" DOUBLE_LINE_SEP "'"
 872	  "\\D'l |\\n[%1]u",
 873	  column_divide_reg(start_col));
 874  if (double_vrule_on_right) {
 875    prints(double_vrule_on_left == 1 ? "+" : "-");
 876    prints(HALF_DOUBLE_LINE_SEP);
 877  }
 878  prints(" 0'");
 879  prints("\\s0"
 880	 "\\v'-" HALF_DOUBLE_LINE_SEP "'");
 881  if (!dont_move)
 882    prints("\\v'" BAR_HEIGHT "'");
 883}
 884
 885double_line_entry *double_line_entry::to_double_line_entry()
 886{
 887  return this;
 888}
 889
 890short_line_entry::short_line_entry(const entry_modifier *m)
 891: simple_entry(m)
 892{
 893}
 894
 895int short_line_entry::line_type()
 896{
 897  return 1;
 898}
 899
 900void short_line_entry::simple_print(int dont_move)
 901{
 902  if (mod->stagger)
 903    prints("\\v'-.5v'");
 904  if (!dont_move)
 905    prints("\\v'-" BAR_HEIGHT "'");
 906  printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
 907  printfs("\\s[\\n[" LINESIZE_REG "]]"
 908	  "\\D'l \\n[%1]u 0'"
 909	  "\\s0",
 910	  span_width_reg(start_col, end_col));
 911  if (!dont_move)
 912    prints("\\v'" BAR_HEIGHT "'");
 913  if (mod->stagger)
 914    prints("\\v'.5v'");
 915}
 916
 917short_double_line_entry::short_double_line_entry(const entry_modifier *m)
 918: simple_entry(m)
 919{
 920}
 921
 922int short_double_line_entry::line_type()
 923{
 924  return 2;
 925}
 926
 927void short_double_line_entry::simple_print(int dont_move)
 928{
 929  if (mod->stagger)
 930    prints("\\v'-.5v'");
 931  if (!dont_move)
 932    prints("\\v'-" BAR_HEIGHT "'");
 933  printfs("\\h'|\\n[%2]u'"
 934	  "\\v'-" HALF_DOUBLE_LINE_SEP "'"
 935	  "\\s[\\n[" LINESIZE_REG "]]"
 936	  "\\D'l \\n[%1]u 0'"
 937	  "\\v'" DOUBLE_LINE_SEP "'"
 938	  "\\D'l |\\n[%2]u 0'"
 939	  "\\s0"
 940	  "\\v'-" HALF_DOUBLE_LINE_SEP "'",
 941	  span_width_reg(start_col, end_col),
 942	  column_start_reg(start_col));
 943  if (!dont_move)
 944    prints("\\v'" BAR_HEIGHT "'");
 945  if (mod->stagger)
 946    prints("\\v'.5v'");
 947}
 948
 949void set_modifier(const entry_modifier *m)
 950{
 951  if (!m->font.empty())
 952    printfs(".ft %1\n", m->font);
 953  if (m->point_size.val != 0) {
 954    prints(".ps ");
 955    if (m->point_size.inc > 0)
 956      prints('+');
 957    else if (m->point_size.inc < 0)
 958      prints('-');
 959    printfs("%1\n", as_string(m->point_size.val));
 960  }
 961  if (m->vertical_spacing.val != 0) {
 962    prints(".vs ");
 963    if (m->vertical_spacing.inc > 0)
 964      prints('+');
 965    else if (m->vertical_spacing.inc < 0)
 966      prints('-');
 967    printfs("%1\n", as_string(m->vertical_spacing.val));
 968  }
 969  if (!m->macro.empty())
 970    printfs(".%1\n", m->macro);
 971}
 972
 973void set_inline_modifier(const entry_modifier *m)
 974{
 975  if (!m->font.empty())
 976    printfs("\\f[%1]", m->font);
 977  if (m->point_size.val != 0) {
 978    prints("\\s[");
 979    if (m->point_size.inc > 0)
 980      prints('+');
 981    else if (m->point_size.inc < 0)
 982      prints('-');
 983    printfs("%1]", as_string(m->point_size.val));
 984  }
 985  if (m->stagger)
 986    prints("\\v'-.5v'");
 987}
 988
 989void restore_inline_modifier(const entry_modifier *m)
 990{
 991  if (!m->font.empty())
 992    prints("\\f[\\n[" SAVED_FONT_REG "]]");
 993  if (m->point_size.val != 0)
 994    prints("\\s[\\n[" SAVED_SIZE_REG "]]");
 995  if (m->stagger)
 996    prints("\\v'.5v'");
 997}
 998
 999struct stuff {
1000  stuff *next;
1001  int row;			// occurs before row `row'
1002  char printed;			// has it been printed?
1003
1004  stuff(int);
1005  virtual void print(table *) = 0;
1006  virtual ~stuff();
1007  virtual int is_single_line() { return 0; };
1008  virtual int is_double_line() { return 0; };
1009};
1010
1011stuff::stuff(int r) : next(0), row(r), printed(0)
1012{
1013}
1014
1015stuff::~stuff()
1016{
1017}
1018
1019struct text_stuff : public stuff {
1020  string contents;
1021  const char *filename;
1022  int lineno;
1023
1024  text_stuff(const string &, int r, const char *fn, int ln);
1025  ~text_stuff();
1026  void print(table *);
1027};
1028
1029text_stuff::text_stuff(const string &s, int r, const char *fn, int ln)
1030: stuff(r), contents(s), filename(fn), lineno(ln)
1031{
1032}
1033
1034text_stuff::~text_stuff()
1035{
1036}
1037
1038void text_stuff::print(table *)
1039{
1040  printed = 1;
1041  prints(".cp \\n(" COMPATIBLE_REG "\n");
1042  set_troff_location(filename, lineno);
1043  prints(contents);
1044  prints(".cp 0\n");
1045  location_force_filename = 1;	// it might have been a .lf command
1046}
1047
1048struct single_hline_stuff : public stuff {
1049  single_hline_stuff(int r);
1050  void print(table *);
1051  int is_single_line();
1052};
1053
1054single_hline_stuff::single_hline_stuff(int r) : stuff(r)
1055{
1056}
1057
1058void single_hline_stuff::print(table *tbl)
1059{
1060  printed = 1;
1061  tbl->print_single_hline(row);
1062}
1063
1064int single_hline_stuff::is_single_line()
1065{
1066  return 1;
1067}
1068
1069struct double_hline_stuff : stuff {
1070  double_hline_stuff(int r);
1071  void print(table *);
1072  int is_double_line();
1073};
1074
1075double_hline_stuff::double_hline_stuff(int r) : stuff(r)
1076{
1077}
1078
1079void double_hline_stuff::print(table *tbl)
1080{
1081  printed = 1;
1082  tbl->print_double_hline(row);
1083}
1084
1085int double_hline_stuff::is_double_line()
1086{
1087  return 1;
1088}
1089
1090struct vertical_rule {
1091  vertical_rule *next;
1092  int start_row;
1093  int end_row;
1094  int col;
1095  char is_double;
1096  string top_adjust;
1097  string bot_adjust;
1098
1099  vertical_rule(int sr, int er, int c, int dbl, vertical_rule *);
1100  ~vertical_rule();
1101  void contribute_to_bottom_macro(table *);
1102  void print();
1103};
1104
1105vertical_rule::vertical_rule(int sr, int er, int c, int dbl, vertical_rule *p)
1106: next(p), start_row(sr), end_row(er), col(c), is_double(dbl)
1107{
1108}
1109
1110vertical_rule::~vertical_rule()
1111{
1112}
1113
1114void vertical_rule::contribute_to_bottom_macro(table *tbl)
1115{
1116  printfs(".if \\n[" CURRENT_ROW_REG "]>=%1",
1117	  as_string(start_row));
1118  if (end_row != tbl->get_nrows() - 1)
1119    printfs("&(\\n[" CURRENT_ROW_REG "]<%1)",
1120	    as_string(end_row));
1121  prints(" \\{");
1122  printfs(".if %1<=\\n[" LAST_PASSED_ROW_REG "] .nr %2 \\n[#T]\n",
1123	  as_string(start_row),
1124	  row_top_reg(start_row));
1125  const char *offset_table[3];
1126  if (is_double) {
1127    offset_table[0] = "-" HALF_DOUBLE_LINE_SEP;
1128    offset_table[1] = "+" HALF_DOUBLE_LINE_SEP;
1129    offset_table[2] = 0;
1130  }
1131  else {
1132    offset_table[0] = "";
1133    offset_table[1] = 0;
1134  }
1135  for (const char **offsetp = offset_table; *offsetp; offsetp++) {
1136    prints(".sp -1\n"
1137	   "\\v'" BODY_DEPTH);
1138    if (!bot_adjust.empty())
1139      printfs("+%1", bot_adjust);
1140    prints("'");
1141    printfs("\\h'\\n[%1]u%3'\\s[\\n[" LINESIZE_REG "]]\\D'l 0 |\\n[%2]u-1v",
1142	    column_divide_reg(col),
1143	    row_top_reg(start_row),
1144	    *offsetp);
1145    if (!bot_adjust.empty())
1146      printfs("-(%1)", bot_adjust);
1147    // don't perform the top adjustment if the top is actually #T
1148    if (!top_adjust.empty())
1149      printfs("+((%1)*(%2>\\n[" LAST_PASSED_ROW_REG "]))",
1150	      top_adjust,
1151	      as_string(start_row));
1152    prints("'\\s0\n");
1153  }
1154  prints(".\\}\n");
1155}
1156
1157void vertical_rule::print()
1158{
1159  printfs("\\*[" TRANSPARENT_STRING_NAME "]"
1160	  ".if %1<=\\*[" QUOTE_STRING_NAME "]\\n[" LAST_PASSED_ROW_REG "] "
1161	  ".nr %2 \\*[" QUOTE_STRING_NAME "]\\n[#T]\n",
1162	  as_string(start_row),
1163	  row_top_reg(start_row));
1164  const char *offset_table[3];
1165  if (is_double) {
1166    offset_table[0] = "-" HALF_DOUBLE_LINE_SEP;
1167    offset_table[1] = "+" HALF_DOUBLE_LINE_SEP;
1168    offset_table[2] = 0;
1169  }
1170  else {
1171    offset_table[0] = "";
1172    offset_table[1] = 0;
1173  }
1174  for (const char **offsetp = offset_table; *offsetp; offsetp++) {
1175    prints("\\*[" TRANSPARENT_STRING_NAME "].sp -1\n"
1176	   "\\*[" TRANSPARENT_STRING_NAME "]\\v'" BODY_DEPTH);
1177    if (!bot_adjust.empty())
1178      printfs("+%1", bot_adjust);
1179    prints("'");
1180    printfs("\\h'\\n[%1]u%3'"
1181	    "\\s[\\n[" LINESIZE_REG "]]"
1182	    "\\D'l 0 |\\*[" QUOTE_STRING_NAME "]\\n[%2]u-1v",
1183	    column_divide_reg(col),
1184	    row_top_reg(start_row),
1185	    *offsetp);
1186    if (!bot_adjust.empty())
1187      printfs("-(%1)", bot_adjust);
1188    // don't perform the top adjustment if the top is actually #T
1189    if (!top_adjust.empty())
1190      printfs("+((%1)*(%2>\\*[" QUOTE_STRING_NAME "]\\n["
1191	      LAST_PASSED_ROW_REG "]))",
1192	      top_adjust,
1193	      as_string(start_row));
1194    prints("'"
1195	   "\\s0\n");
1196  }
1197}
1198
1199table::table(int nc, unsigned f, int ls, char dpc)
1200: flags(f), nrows(0), ncolumns(nc), linesize(ls), decimal_point_char(dpc),
1201  vrule_list(0), stuff_list(0), span_list(0),
1202  entry_list(0), entry_list_tailp(&entry_list), entry(0),
1203  vline(0), row_is_all_lines(0), left_separation(0), right_separation(0),
1204  allocated_rows(0)
1205{
1206  minimum_width = new string[ncolumns];
1207  column_separation = ncolumns > 1 ? new int[ncolumns - 1] : 0;
1208  equal = new char[ncolumns];
1209  int i;
1210  for (i = 0; i < ncolumns; i++)
1211    equal[i] = 0;
1212  for (i = 0; i < ncolumns-1; i++)
1213    column_separation[i] = DEFAULT_COLUMN_SEPARATION;
1214  delim[0] = delim[1] = '\0';
1215}
1216
1217table::~table()
1218{
1219  for (int i = 0; i < nrows; i++) {
1220    a_delete entry[i];
1221    a_delete vline[i];
1222  }
1223  a_delete entry;
1224  a_delete vline;
1225  while (entry_list) {
1226    table_entry *tem = entry_list;
1227    entry_list = entry_list->next;
1228    delete tem;
1229  }
1230  ad_delete(ncolumns) minimum_width;
1231  a_delete column_separation;
1232  a_delete equal;
1233  while (stuff_list) {
1234    stuff *tem = stuff_list;
1235    stuff_list = stuff_list->next;
1236    delete tem;
1237  }
1238  while (vrule_list) {
1239    vertical_rule *tem = vrule_list;
1240    vrule_list = vrule_list->next;
1241    delete tem;
1242  }
1243  a_delete row_is_all_lines;
1244  while (span_list) {
1245    horizontal_span *tem = span_list;
1246    span_list = span_list->next;
1247    delete tem;
1248  }
1249}
1250
1251void table::set_delim(char c1, char c2)
1252{
1253  delim[0] = c1;
1254  delim[1] = c2;
1255}
1256
1257void table::set_minimum_width(int c, const string &w)
1258{
1259  assert(c >= 0 && c < ncolumns);
1260  minimum_width[c] = w;
1261}
1262
1263void table::set_column_separation(int c, int n)
1264{
1265  assert(c >= 0 && c < ncolumns - 1);
1266  column_separation[c] = n;
1267}
1268
1269void table::set_equal_column(int c)
1270{
1271  assert(c >= 0 && c < ncolumns);
1272  equal[c] = 1;
1273}
1274
1275void table::add_stuff(stuff *p)
1276{
1277  stuff **pp;
1278  for (pp = &stuff_list; *pp; pp = &(*pp)->next)
1279    ;
1280  *pp = p;
1281}
1282
1283void table::add_text_line(int r, const string &s, const char *filename, int lineno)
1284{
1285  add_stuff(new text_stuff(s, r, filename, lineno));
1286}
1287
1288void table::add_single_hline(int r)
1289{
1290  add_stuff(new single_hline_stuff(r));
1291}
1292
1293void table::add_double_hline(int r)
1294{
1295  add_stuff(new double_hline_stuff(r));
1296}
1297
1298void table::allocate(int r)
1299{
1300  if (r >= nrows) {
1301    typedef table_entry **PPtable_entry; // work around g++ 1.36.1 bug
1302    if (r >= allocated_rows) {
1303      if (allocated_rows == 0) {
1304	allocated_rows = 16;
1305	if (allocated_rows <= r)
1306	  allocated_rows = r + 1;
1307	entry = new PPtable_entry[allocated_rows];
1308	vline = new char*[allocated_rows];
1309      }
1310      else {
1311	table_entry ***old_entry = entry;
1312	int old_allocated_rows = allocated_rows;
1313	allocated_rows *= 2;
1314	if (allocated_rows <= r)
1315	  allocated_rows = r + 1;
1316	entry = new PPtable_entry[allocated_rows];
1317	memcpy(entry, old_entry, sizeof(table_entry**)*old_allocated_rows);
1318	a_delete old_entry;
1319	char **old_vline = vline;
1320	vline = new char*[allocated_rows];
1321	memcpy(vline, old_vline, sizeof(char*)*old_allocated_rows);
1322	a_delete old_vline;
1323      }
1324    }
1325    assert(allocated_rows > r);
1326    while (nrows <= r) {
1327      entry[nrows] = new table_entry*[ncolumns];
1328      int i;
1329      for (i = 0; i < ncolumns; i++)
1330	entry[nrows][i] = 0;
1331      vline[nrows] = new char[ncolumns+1];
1332      for (i = 0; i < ncolumns+1; i++)
1333	vline[nrows][i] = 0;
1334      nrows++;
1335    }
1336  }
1337}
1338
1339void table::do_hspan(int r, int c)
1340{
1341  assert(r >= 0 && c >= 0 && r < nrows && c < ncolumns);
1342  if (c == 0) {
1343    error("first column cannot be horizontally spanned");
1344    return;
1345  }
1346  table_entry *e = entry[r][c];
1347  if (e) {
1348    assert(e->start_row <= r && r <= e->end_row
1349	   && e->start_col <= c && c <= e->end_col
1350	   && e->end_row - e->start_row > 0
1351	   && e->end_col - e->start_col > 0);
1352    return;
1353  }
1354  e = entry[r][c-1];
1355  // e can be 0 if we had an empty entry or an error
1356  if (e == 0)
1357    return;
1358  if (e->start_row != r) {
1359    /*
1360      l l
1361      ^ s */
1362    error("impossible horizontal span at row %1, column %2", r + 1, c + 1);
1363  }
1364  else {
1365    e->end_col = c;
1366    entry[r][c] = e;
1367  }
1368}
1369
1370void table::do_vspan(int r, int c)
1371{
1372  assert(r >= 0 && c >= 0 && r < nrows && c < ncolumns);
1373  if (r == 0) {
1374    error("first row cannot be vertically spanned");
1375    return;
1376  }
1377  table_entry *e = entry[r][c];
1378  if (e) {
1379    assert(e->start_row <= r && r <= e->end_row
1380	   && e->start_col <= c && c <= e->end_col
1381	   && e->end_row - e->start_row > 0
1382	   && e->end_col - e->start_col > 0);
1383    return;
1384  }
1385  e = entry[r-1][c];
1386  // e can be 0 if we had an empty entry or an error
1387  if (e == 0)
1388    return;
1389  if (e->start_col != c) {
1390    /* l s
1391       l ^ */
1392    error("impossible vertical span at row %1, column %2", r + 1, c + 1);
1393  }
1394  else {
1395    for (int i = c; i <= e->end_col; i++) {
1396      assert(entry[r][i] == 0);
1397      entry[r][i] = e;
1398    }
1399    e->end_row = r;
1400  }
1401}
1402
1403int find_decimal_point(const char *s, char decimal_point_char,
1404		       const char *delim)
1405{
1406  if (s == 0 || *s == '\0')
1407    return -1;
1408  const char *p;
1409  int in_delim = 0;		// is p within eqn delimiters?
1410  // tbl recognises \& even within eqn delimiters; I don't
1411  for (p = s; *p; p++)
1412    if (in_delim) {
1413      if (*p == delim[1])
1414	in_delim = 0;
1415    }
1416    else if (*p == delim[0])
1417      in_delim = 1;
1418    else if (p[0] == '\\' && p[1] == '&')
1419      return p - s;
1420  int possible_pos = -1;
1421  in_delim = 0;
1422  for (p = s; *p; p++)
1423    if (in_delim) {
1424      if (*p == delim[1])
1425	in_delim = 0;
1426    }
1427    else if (*p == delim[0])
1428      in_delim = 1;
1429    else if (p[0] == decimal_point_char && csdigit(p[1]))
1430      possible_pos = p - s;
1431  if (possible_pos >= 0)
1432    return possible_pos;
1433  in_delim = 0;
1434  for (p = s; *p; p++)
1435    if (in_delim) {
1436      if (*p == delim[1])
1437	in_delim = 0;
1438    }
1439    else if (*p == delim[0])
1440      in_delim = 1;
1441    else if (csdigit(*p))
1442      possible_pos = p + 1 - s;
1443  return possible_pos;
1444}
1445
1446void table::add_entry(int r, int c, const string &str, const entry_format *f,
1447		      const char *fn, int ln)
1448{
1449  allocate(r);
1450  table_entry *e = 0;
1451  if (str == "\\_") {
1452    e = new short_line_entry(f);
1453  }
1454  else if (str == "\\=") {
1455    e = new short_double_line_entry(f);
1456  }
1457  else if (str == "_") {
1458    single_line_entry *lefte;
1459    if (c > 0 && entry[r][c-1] != 0 &&
1460	(lefte = entry[r][c-1]->to_single_line_entry()) != 0
1461	&& lefte->start_row == r
1462	&& lefte->mod->stagger == f->stagger) {
1463      lefte->end_col = c;
1464      entry[r][c] = lefte;
1465    }
1466    else
1467      e = new single_line_entry(f);
1468  }
1469  else if (str == "=") {
1470    double_line_entry *lefte;
1471    if (c > 0 && entry[r][c-1] != 0 &&
1472	(lefte = entry[r][c-1]->to_double_line_entry()) != 0
1473	&& lefte->start_row == r
1474	&& lefte->mod->stagger == f->stagger) {
1475      lefte->end_col = c;
1476      entry[r][c] = lefte;
1477    }
1478    else
1479      e = new double_line_entry(f);
1480  }
1481  else if (str == "\\^") {
1482    do_vspan(r, c);
1483  }
1484  else if (str.length() > 2 && str[0] == '\\' && str[1] == 'R') {
1485    if (str.search('\n') >= 0)
1486      error_with_file_and_line(fn, ln, "bad repeated character");
1487    else {
1488      char *s = str.substring(2, str.length() - 2).extract();
1489      e = new repeated_char_entry(s, f);
1490    }
1491  }
1492  else {
1493    int is_block = str.search('\n') >= 0;
1494    char *s;
1495    switch (f->type) {
1496    case FORMAT_SPAN:
1497      assert(str.empty());
1498      do_hspan(r, c);
1499      break;
1500    case FORMAT_LEFT:
1501      if (!str.empty()) {
1502	s = str.extract();
1503	if (is_block)
1504	  e = new left_block_entry(s, f);
1505	else
1506	  e = new left_text_entry(s, f);
1507      }
1508      else
1509	e = new empty_entry(f);
1510      break;
1511    case FORMAT_CENTER:
1512      if (!str.empty()) {
1513	s = str.extract();
1514	if (is_block)
1515	  e = new center_block_entry(s, f);
1516	else
1517	  e = new center_text_entry(s, f);
1518      }
1519      else
1520	e = new empty_entry(f);
1521      break;
1522    case FORMAT_RIGHT:
1523      if (!str.empty()) {
1524	s = str.extract();
1525	if (is_block)
1526	  e = new right_block_entry(s, f);
1527	else
1528	  e = new right_text_entry(s, f);
1529      }
1530      else
1531	e = new empty_entry(f);
1532      break;
1533    case FORMAT_NUMERIC:
1534      if (!str.empty()) {
1535	s = str.extract();
1536	if (is_block) {
1537	  error_with_file_and_line(fn, ln, "can't have numeric text block");
1538	  e = new left_block_entry(s, f);
1539	}
1540	else {
1541	  int pos = find_decimal_point(s, decimal_point_char, delim);
1542	  if (pos < 0)
1543	    e = new center_text_entry(s, f);
1544	  else
1545	    e = new numeric_text_entry(s, f, pos);
1546	}
1547      }
1548      else
1549	e = new empty_entry(f);
1550      break;
1551    case FORMAT_ALPHABETIC:
1552      if (!str.empty()) {
1553	s = str.extract();
1554	if (is_block)
1555	  e = new alphabetic_block_entry(s, f);
1556	else
1557	  e = new alphabetic_text_entry(s, f);
1558      }
1559      else
1560	e = new empty_entry(f);
1561      break;
1562    case FORMAT_VSPAN:
1563      do_vspan(r, c);
1564      break;
1565    case FORMAT_HLINE:
1566      if (str.length() != 0)
1567	error_with_file_and_line(fn, ln,
1568				 "non-empty data entry for `_' format ignored");
1569      e = new single_line_entry(f);
1570      break;
1571    case FORMAT_DOUBLE_HLINE:
1572      if (str.length() != 0)
1573	error_with_file_and_line(fn, ln,
1574				 "non-empty data entry for `=' format ignored");
1575      e = new double_line_entry(f);
1576      break;
1577    default:
1578      assert(0);
1579    }
1580  }
1581  if (e) {
1582    table_entry *preve = entry[r][c];
1583    if (preve) {
1584      /* c s
1585         ^ l */
1586      error_with_file_and_line(fn, ln, "row %1, column %2 already spanned",
1587			       r + 1, c + 1);
1588      delete e;
1589    }
1590    else {
1591      e->input_lineno = ln;
1592      e->input_filename = fn;
1593      e->start_row = e->end_row = r;
1594      e->start_col = e->end_col = c;
1595      *entry_list_tailp = e;
1596      entry_list_tailp = &e->next;
1597      entry[r][c] = e;
1598    }
1599  }
1600}
1601
1602// add vertical lines for row r
1603
1604void table::add_vlines(int r, const char *v)
1605{
1606  allocate(r);
1607  for (int i = 0; i < ncolumns+1; i++)
1608    vline[r][i] = v[i];
1609}
1610
1611void table::check()
1612{
1613  table_entry *p = entry_list;
1614  int i, j;
1615  while (p) {
1616    for (i = p->start_row; i <= p->end_row; i++)
1617      for (j = p->start_col; j <= p->end_col; j++)
1618	assert(entry[i][j] == p);
1619    p = p->next;
1620  }
1621}
1622
1623void table::print()
1624{
1625  location_force_filename = 1;
1626  check();
1627  init_output();
1628  determine_row_type();
1629  compute_widths();
1630  if (!(flags & CENTER))
1631    prints(".if \\n[" SAVED_CENTER_REG "] \\{");
1632  prints(".in +(u;\\n[.l]-\\n[.i]-\\n[TW]/2>?-\\n[.i])\n"
1633	 ".nr " SAVED_INDENT_REG " \\n[.i]\n");
1634  if (!(flags & CENTER))
1635    prints(".\\}\n");
1636  build_vrule_list();
1637  define_bottom_macro();
1638  do_top();
1639  for (int i = 0; i < nrows; i++)
1640    do_row(i);
1641  do_bottom();
1642}
1643
1644void table::determine_row_type()
1645{
1646  row_is_all_lines = new char[nrows];
1647  for (int i = 0; i < nrows; i++) {
1648    int had_single = 0;
1649    int had_double = 0;
1650    int had_non_line = 0;
1651    for (int c = 0; c < ncolumns; c++) {
1652      table_entry *e = entry[i][c];
1653      if (e != 0) {
1654	if (e->start_row == e->end_row) {
1655	  int t = e->line_type();
1656	  switch (t) {
1657	  case -1:
1658	    had_non_line = 1;
1659	    break;
1660	  case 0:
1661	    // empty
1662	    break;
1663	  case 1:
1664	    had_single = 1;
1665	    break;
1666	  case 2:
1667	    had_double = 1;
1668	    break;
1669	  default:
1670	    assert(0);
1671	  }
1672	  if (had_non_line)
1673	    break;
1674	}
1675	c = e->end_col;
1676      }
1677    }
1678    if (had_non_line)
1679      row_is_all_lines[i] = 0;
1680    else if (had_double)
1681      row_is_all_lines[i] = 2;
1682    else if (had_single)
1683      row_is_all_lines[i] = 1;
1684    else
1685      row_is_all_lines[i] = 0;
1686  }
1687}
1688
1689void table::init_output()
1690{
1691  prints(".nr " COMPATIBLE_REG " \\n(.C\n"
1692	 ".cp 0\n");
1693  if (linesize > 0)
1694    printfs(".nr " LINESIZE_REG " %1\n", as_string(linesize));
1695  else
1696    prints(".nr " LINESIZE_REG " \\n[.s]\n");
1697  if (!(flags & CENTER))
1698    prints(".nr " SAVED_CENTER_REG " \\n[.ce]\n");
1699  if (compatible_flag)
1700    prints(".ds " LEADER_REG " \\a\n");
1701  prints(".de " RESET_MACRO_NAME "\n"
1702	 ".ft \\n[.f]\n"
1703	 ".ps \\n[.s]\n"
1704	 ".vs \\n[.v]u\n"
1705	 ".in \\n[.i]u\n"
1706	 ".ll \\n[.l]u\n"
1707	 ".ls \\n[.L]\n"
1708	 ".ad \\n[.j]\n"
1709	 ".ie \\n[.u] .fi\n"
1710	 ".el .nf\n"
1711	 ".ce \\n[.ce]\n"
1712	 "..\n"
1713	 ".nr " SAVED_INDENT_REG " \\n[.i]\n"
1714	 ".nr " SAVED_FONT_REG " \\n[.f]\n"
1715	 ".nr " SAVED_SIZE_REG " \\n[.s]\n"
1716	 ".nr " SAVED_FILL_REG " \\n[.u]\n"
1717	 ".nr T. 0\n"
1718	 ".nr " CURRENT_ROW_REG " 0-1\n"
1719	 ".nr " LAST_PASSED_ROW_REG " 0-1\n"
1720	 ".nr " SECTION_DIVERSION_FLAG_REG " 0\n"
1721	 ".ds " TRANSPARENT_STRING_NAME "\n"
1722	 ".ds " QUOTE_STRING_NAME "\n"
1723	 ".nr " NEED_BOTTOM_RULE_REG " 1\n"
1724	 ".nr " SUPPRESS_BOTTOM_REG " 0\n"
1725	 ".eo\n"
1726	 ".de " REPEATED_MARK_MACRO "\n"
1727	 ".mk \\$1\n"
1728	 ".if !'\\n(.z'' \\!." REPEATED_MARK_MACRO " \"\\$1\"\n"
1729	 "..\n"
1730	 ".de " REPEATED_VPT_MACRO "\n"
1731	 ".vpt \\$1\n"
1732	 ".if !'\\n(.z'' \\!." REPEATED_VPT_MACRO " \"\\$1\"\n"
1733	 "..\n");
1734  if (!(flags & NOKEEP))
1735    prints(".de " KEEP_MACRO_NAME "\n"
1736	   ".if '\\n[.z]'' \\{.ds " QUOTE_STRING_NAME " \\\\\n"
1737	   ".ds " TRANSPARENT_STRING_NAME " \\!\n"
1738	   ".di " SECTION_DIVERSION_NAME "\n"
1739	   ".nr " SECTION_DIVERSION_FLAG_REG " 1\n"
1740	   ".in 0\n"
1741	   ".\\}\n"
1742	   "..\n"
1743	   ".de " RELEASE_MACRO_NAME "\n"
1744	   ".if \\n[" SECTION_DIVERSION_FLAG_REG "] \\{"
1745	   ".di\n"
1746	   ".in \\n[" SAVED_INDENT_REG "]u\n"
1747	   ".nr " SAVED_DN_REG " \\n[dn]\n"
1748	   ".ds " QUOTE_STRING_NAME "\n"
1749	   ".ds " TRANSPARENT_STRING_NAME "\n"
1750	   ".nr " SECTION_DIVERSION_FLAG_REG " 0\n"
1751	   ".if \\n[.t]<=\\n[dn] \\{"
1752	   ".nr T. 1\n"
1753	   ".T#\n"
1754	   ".nr " SUPPRESS_BOTTOM_REG " 1\n"
1755	   ".sp \\n[.t]u\n"
1756	   ".nr " SUPPRESS_BOTTOM_REG " 0\n"
1757	   ".mk #T\n"
1758	   ".\\}\n"
1759	   ".if \\n[.t]<=\\n[" SAVED_DN_REG "] "
1760	   /* Since we turn off traps, it won't get into an infinite loop
1761	   when we try and print it; it will just go off the bottom of the
1762	   page. */
1763	   ".tm warning: page \\n%: table text block will not fit on one page\n"
1764	   ".nf\n"
1765	   ".ls 1\n"
1766	   "." SECTION_DIVERSION_NAME "\n"
1767	   ".ls\n"
1768	   ".rm " SECTION_DIVERSION_NAME "\n"
1769	   ".\\}\n"
1770	   "..\n"
1771	   ".nr " TABLE_DIVERSION_FLAG_REG " 0\n"
1772	   ".de " TABLE_KEEP_MACRO_NAME "\n"
1773	   ".if '\\n[.z]'' \\{"
1774	   ".di " TABLE_DIVERSION_NAME "\n"
1775	   ".nr " TABLE_DIVERSION_FLAG_REG " 1\n"
1776	   ".\\}\n"
1777	   "..\n"
1778	   ".de " TABLE_RELEASE_MACRO_NAME "\n"
1779	   ".if \\n[" TABLE_DIVERSION_FLAG_REG "] \\{.br\n"
1780	   ".di\n"
1781	   ".nr " SAVED_DN_REG " \\n[dn]\n"
1782	   ".ne \\n[dn]u+\\n[.V]u\n"
1783	   ".ie \\n[.t]<=\\n[" SAVED_DN_REG "] "
1784	   ".tm error: page \\n%: table will not fit on one page; use .TS H/.TH with a supporting macro package\n"
1785	   ".el \\{"
1786	   ".in 0\n"
1787	   ".ls 1\n"
1788	   ".nf\n"
1789	   "." TABLE_DIVERSION_NAME "\n"
1790	   ".\\}\n"
1791	   ".rm " TABLE_DIVERSION_NAME "\n"
1792	   ".\\}\n"
1793	   "..\n");
1794  prints(".ec\n"
1795	 ".ce 0\n"
1796	 ".nf\n");
1797}
1798
1799string block_width_reg(int r, int c)
1800{
1801  static char name[sizeof(BLOCK_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1802  sprintf(name, BLOCK_WIDTH_PREFIX "%d,%d", r, c);
1803  return string(name);
1804}
1805
1806string block_diversion_name(int r, int c)
1807{
1808  static char name[sizeof(BLOCK_DIVERSION_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1809  sprintf(name, BLOCK_DIVERSION_PREFIX "%d,%d", r, c);
1810  return string(name);
1811}
1812
1813string block_height_reg(int r, int c)
1814{
1815  static char name[sizeof(BLOCK_HEIGHT_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1816  sprintf(name, BLOCK_HEIGHT_PREFIX "%d,%d", r, c);
1817  return string(name);
1818}
1819
1820string span_width_reg(int start_col, int end_col)
1821{
1822  static char name[sizeof(SPAN_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1823  sprintf(name, SPAN_WIDTH_PREFIX "%d", start_col);
1824  if (end_col != start_col)
1825    sprintf(strchr(name, '\0'), ",%d", end_col);
1826  return string(name);
1827}
1828
1829string span_left_numeric_width_reg(int start_col, int end_col)
1830{
1831  static char name[sizeof(SPAN_LEFT_NUMERIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1832  sprintf(name, SPAN_LEFT_NUMERIC_WIDTH_PREFIX "%d", start_col);
1833  if (end_col != start_col)
1834    sprintf(strchr(name, '\0'), ",%d", end_col);
1835  return string(name);
1836}
1837
1838string span_right_numeric_width_reg(int start_col, int end_col)
1839{
1840  static char name[sizeof(SPAN_RIGHT_NUMERIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1841  sprintf(name, SPAN_RIGHT_NUMERIC_WIDTH_PREFIX "%d", start_col);
1842  if (end_col != start_col)
1843    sprintf(strchr(name, '\0'), ",%d", end_col);
1844  return string(name);
1845}
1846
1847string span_alphabetic_width_reg(int start_col, int end_col)
1848{
1849  static char name[sizeof(SPAN_ALPHABETIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
1850  sprintf(name, SPAN_ALPHABETIC_WIDTH_PREFIX "%d", start_col);
1851  if (end_col != start_col)
1852    sprintf(strchr(name, '\0'), ",%d", end_col);
1853  return string(name);
1854}
1855
1856string column_separation_reg(int col)
1857{
1858  static char name[sizeof(COLUMN_SEPARATION_PREFIX)+INT_DIGITS];
1859  sprintf(name, COLUMN_SEPARATION_PREFIX "%d", col);
1860  return string(name);
1861}
1862
1863string row_start_reg(int row)
1864{
1865  static char name[sizeof(ROW_START_PREFIX)+INT_DIGITS];
1866  sprintf(name, ROW_START_PREFIX "%d", row);
1867  return string(name);
1868}  
1869
1870string column_start_reg(int col)
1871{
1872  static char name[sizeof(COLUMN_START_PREFIX)+INT_DIGITS];
1873  sprintf(name, COLUMN_START_PREFIX "%d", col);
1874  return string(name);
1875}  
1876
1877string column_end_reg(int col)
1878{
1879  static char name[sizeof(COLUMN_END_PREFIX)+INT_DIGITS];
1880  sprintf(name, COLUMN_END_PREFIX "%d", col);
1881  return string(name);
1882}
1883
1884string column_divide_reg(int col)
1885{
1886  static char name[sizeof(COLUMN_DIVIDE_PREFIX)+INT_DIGITS];
1887  sprintf(name, COLUMN_DIVIDE_PREFIX "%d", col);
1888  return string(name);
1889}
1890
1891string row_top_reg(int row)
1892{
1893  static char name[sizeof(ROW_TOP_PREFIX)+INT_DIGITS];
1894  sprintf(name, ROW_TOP_PREFIX "%d", row);
1895  return string(name);
1896}
1897
1898void init_span_reg(int start_col, int end_col)
1899{
1900  printfs(".nr %1 \\n(.H\n.nr %2 0\n.nr %3 0\n.nr %4 0\n",
1901	  span_width_reg(start_col, end_col),
1902	  span_alphabetic_width_reg(start_col, end_col),
1903	  span_left_numeric_width_reg(start_col, end_col),
1904	  span_right_numeric_width_reg(start_col, end_col));
1905}
1906
1907void compute_span_width(int start_col, int end_col)
1908{
1909  printfs(".nr %1 \\n[%1]>?(\\n[%2]+\\n[%3])\n"
1910	  ".if \\n[%4] .nr %1 \\n[%1]>?(\\n[%4]+2n)\n", 
1911	  span_width_reg(start_col, end_col),
1912	  span_left_numeric_width_reg(start_col, end_col),
1913	  span_right_numeric_width_reg(start_col, end_col),
1914	  span_alphabetic_width_reg(start_col, end_col));
1915}
1916
1917// Increase the widths of columns so that the width of any spanning entry
1918// is not greater than the sum of the widths of the columns that it spans.
1919// Ensure that the widths of columns remain equal.
1920
1921void table::divide_span(int start_col, int end_col)
1922{
1923  assert(end_col > start_col);
1924  printfs(".nr " NEEDED_REG " \\n[%1]-(\\n[%2]", 
1925	  span_width_reg(start_col, end_col),
1926	  span_width_reg(start_col, start_col));
1927  int i;
1928  for (i = start_col + 1; i <= end_col; i++) {
1929    // The column separation may shrink with the expand option.
1930    if (!(flags & EXPAND))
1931      printfs("+%1n", as_string(column_separation[i - 1]));
1932    printfs("+\\n[%1]", span_width_reg(i, i));
1933  }
1934  prints(")\n");
1935  printfs(".nr " NEEDED_REG " \\n[" NEEDED_REG "]/%1\n",
1936	  as_string(end_col - start_col + 1));
1937  prints(".if \\n[" NEEDED_REG "] \\{");
1938  for (i = start_col; i <= end_col; i++)
1939    printfs(".nr %1 +\\n[" NEEDED_REG "]\n", 
1940	    span_width_reg(i, i));
1941  int equal_flag = 0;
1942  for (i = start_col; i <= end_col && !equal_flag; i++)
1943    if (equal[i])
1944      equal_flag = 1;
1945  if (equal_flag) {
1946    for (i = 0; i < ncolumns; i++)
1947      if (i < start_col || i > end_col)
1948	printfs(".nr %1 +\\n[" NEEDED_REG "]\n", 
1949	    span_width_reg(i, i));
1950  }
1951  prints(".\\}\n");
1952}
1953
1954void table::sum_columns(int start_col, int end_col)
1955{
1956  assert(end_col > start_col);
1957  printfs(".nr %1 \\n[%2]", 
1958	  span_width_reg(start_col, end_col),
1959	  span_width_reg(start_col, start_col));
1960  for (int i = start_col + 1; i <= end_col; i++)
1961    printfs("+(%1*\\n[" SEPARATION_FACTOR_REG "])+\\n[%2]",
1962	    as_string(column_separation[i - 1]),
1963	    span_width_reg(i, i));
1964  prints('\n');
1965}
1966
1967horizontal_span::horizontal_span(int sc, int ec, horizontal_span *p)
1968: next(p), start_col(sc), end_col(ec)
1969{
1970}
1971
1972void table::build_span_list()
1973{
1974  span_list = 0;
1975  table_entry *p = entry_list;
1976  while (p) {
1977    if (p->end_col != p->start_col) {
1978      horizontal_span *q;
1979      for (q = span_list; q; q = q->next)
1980	if (q->start_col == p->start_col
1981	    && q->end_col == p->end_col)
1982	  break;
1983      if (!q)
1984	span_list = new horizontal_span(p->start_col, p->end_col, span_list);
1985    }
1986    p = p->next;
1987  }
1988  // Now sort span_list primarily by order of end_row, and secondarily
1989  // by reverse order of start_row. This ensures that if we divide
1990  // spans using the order in span_list, we will get reasonable results.
1991  horizontal_span *unsorted = span_list;
1992  span_list = 0;
1993  while (unsorted) {
1994    horizontal_span **pp;
1995    for (pp = &span_list; *pp; pp = &(*pp)->next)
1996      if (unsorted->end_col < (*pp)->end_col
1997	  || (unsorted->end_col == (*pp)->end_col
1998	      && (unsorted->start_col > (*pp)->start_col)))
1999	break;
2000    horizontal_span *tem = unsorted->next;
2001    unsorted->next = *pp;
2002    *pp = unsorted;
2003    unsorted = tem;
2004  }
2005}
2006
2007void table::compute_separation_factor()
2008{
2009  if (flags & (ALLBOX|BOX|DOUBLEBOX))
2010    left_separation = right_separation = 1;
2011  else {
2012    for (int i = 0; i < nrows; i++) {
2013      if (vline[i][0] > 0)
2014	left_separation = 1;
2015      if (vline[i][ncolumns] > 0)
2016	right_separation = 1;
2017    }
2018  }
2019  if (flags & EXPAND) {
2020    int total_sep = left_separation + right_separation;
2021    int i;
2022    for (i = 0; i < ncolumns - 1; i++)
2023      total_sep += column_separatio

Large files files are truncated, but you can click here to view the full file