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