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