1 /* $NetBSD: column.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 Free Software Foundation, Inc. 5 Written by James Clark (jjc@jclark.com) 6 7 This file is part of groff. 8 9 groff is free software; you can redistribute it and/or modify it under 10 the terms of the GNU General Public License as published by the Free 11 Software Foundation; either version 2, or (at your option) any later 12 version. 13 14 groff is distributed in the hope that it will be useful, but WITHOUT ANY 15 WARRANTY; without even the implied warranty of MERCHANTABILITY or 16 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 17 for more details. 18 19 You should have received a copy of the GNU General Public License along 20 with groff; see the file COPYING. If not, write to the Free Software 21 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */ 22 23 #ifdef COLUMN 24 25 #include "troff.h" 26 #include "symbol.h" 27 #include "dictionary.h" 28 #include "hvunits.h" 29 #include "env.h" 30 #include "request.h" 31 #include "node.h" 32 #include "token.h" 33 #include "div.h" 34 #include "reg.h" 35 #include "stringclass.h" 36 37 void output_file::vjustify(vunits, symbol) 38 { 39 // do nothing 40 } 41 42 struct justification_spec; 43 struct output_line; 44 45 class column : public output_file { 46 private: 47 output_file *out; 48 vunits bottom; 49 output_line *col; 50 output_line **tail; 51 void add_output_line(output_line *); 52 void begin_page(int pageno, vunits page_length); 53 void flush(); 54 void print_line(hunits, vunits, node *, vunits, vunits); 55 void vjustify(vunits, symbol); 56 void transparent_char(unsigned char c); 57 void copy_file(hunits, vunits, const char *); 58 int is_printing(); 59 void check_bottom(); 60 public: 61 column(); 62 ~column(); 63 void start(); 64 void output(); 65 void justify(const justification_spec &); 66 void trim(); 67 void reset(); 68 vunits get_bottom(); 69 vunits get_last_extra_space(); 70 int is_active() { return out != 0; } 71 }; 72 73 column *the_column = 0; 74 75 struct transparent_output_line; 76 struct vjustify_output_line; 77 78 class output_line { 79 output_line *next; 80 public: 81 output_line(); 82 virtual ~output_line(); 83 virtual void output(output_file *, vunits); 84 virtual transparent_output_line *as_transparent_output_line(); 85 virtual vjustify_output_line *as_vjustify_output_line(); 86 virtual vunits distance(); 87 virtual vunits height(); 88 virtual void reset(); 89 virtual vunits extra_space(); // post line 90 friend class column; 91 friend class justification_spec; 92 }; 93 94 class position_output_line : public output_line { 95 vunits dist; 96 public: 97 position_output_line(vunits); 98 vunits distance(); 99 }; 100 101 class node_output_line : public position_output_line { 102 node *nd; 103 hunits page_offset; 104 vunits before; 105 vunits after; 106 public: 107 node_output_line(vunits, node *, hunits, vunits, vunits); 108 ~node_output_line(); 109 void output(output_file *, vunits); 110 vunits height(); 111 vunits extra_space(); 112 }; 113 114 class vjustify_output_line : public position_output_line { 115 vunits current; 116 symbol typ; 117 public: 118 vjustify_output_line(vunits dist, symbol); 119 vunits height(); 120 vjustify_output_line *as_vjustify_output_line(); 121 void vary(vunits amount); 122 void reset(); 123 symbol type(); 124 }; 125 126 inline symbol vjustify_output_line::type() 127 { 128 return typ; 129 } 130 131 class copy_file_output_line : public position_output_line { 132 symbol filename; 133 hunits hpos; 134 public: 135 copy_file_output_line(vunits, const char *, hunits); 136 void output(output_file *, vunits); 137 }; 138 139 class transparent_output_line : public output_line { 140 string buf; 141 public: 142 transparent_output_line(); 143 void output(output_file *, vunits); 144 void append_char(unsigned char c); 145 transparent_output_line *as_transparent_output_line(); 146 }; 147 148 output_line::output_line() : next(0) 149 { 150 } 151 152 output_line::~output_line() 153 { 154 } 155 156 void output_line::reset() 157 { 158 } 159 160 transparent_output_line *output_line::as_transparent_output_line() 161 { 162 return 0; 163 } 164 165 vjustify_output_line *output_line::as_vjustify_output_line() 166 { 167 return 0; 168 } 169 170 void output_line::output(output_file *, vunits) 171 { 172 } 173 174 vunits output_line::distance() 175 { 176 return V0; 177 } 178 179 vunits output_line::height() 180 { 181 return V0; 182 } 183 184 vunits output_line::extra_space() 185 { 186 return V0; 187 } 188 189 position_output_line::position_output_line(vunits d) 190 : dist(d) 191 { 192 } 193 194 vunits position_output_line::distance() 195 { 196 return dist; 197 } 198 199 node_output_line::node_output_line(vunits d, node *n, hunits po, vunits b, vunits a) 200 : position_output_line(d), nd(n), page_offset(po), before(b), after(a) 201 { 202 } 203 204 node_output_line::~node_output_line() 205 { 206 delete_node_list(nd); 207 } 208 209 void node_output_line::output(output_file *out, vunits pos) 210 { 211 out->print_line(page_offset, pos, nd, before, after); 212 nd = 0; 213 } 214 215 vunits node_output_line::height() 216 { 217 return after; 218 } 219 220 vunits node_output_line::extra_space() 221 { 222 return after; 223 } 224 225 vjustify_output_line::vjustify_output_line(vunits d, symbol t) 226 : position_output_line(d), typ(t) 227 { 228 } 229 230 void vjustify_output_line::reset() 231 { 232 current = V0; 233 } 234 235 vunits vjustify_output_line::height() 236 { 237 return current; 238 } 239 240 vjustify_output_line *vjustify_output_line::as_vjustify_output_line() 241 { 242 return this; 243 } 244 245 inline void vjustify_output_line::vary(vunits amount) 246 { 247 current += amount; 248 } 249 250 transparent_output_line::transparent_output_line() 251 { 252 } 253 254 transparent_output_line *transparent_output_line::as_transparent_output_line() 255 { 256 return this; 257 } 258 259 void transparent_output_line::append_char(unsigned char c) 260 { 261 assert(c != 0); 262 buf += c; 263 } 264 265 void transparent_output_line::output(output_file *out, vunits) 266 { 267 int len = buf.length(); 268 for (int i = 0; i < len; i++) 269 out->transparent_char(buf[i]); 270 } 271 272 copy_file_output_line::copy_file_output_line(vunits d, const char *f, hunits h) 273 : position_output_line(d), hpos(h), filename(f) 274 { 275 } 276 277 void copy_file_output_line::output(output_file *out, vunits pos) 278 { 279 out->copy_file(hpos, pos, filename.contents()); 280 } 281 282 column::column() 283 : bottom(V0), col(0), tail(&col), out(0) 284 { 285 } 286 287 column::~column() 288 { 289 assert(out != 0); 290 error("automatically outputting column before exiting"); 291 output(); 292 delete the_output; 293 } 294 295 void column::start() 296 { 297 assert(out == 0); 298 if (!the_output) 299 init_output(); 300 assert(the_output != 0); 301 out = the_output; 302 the_output = this; 303 } 304 305 void column::begin_page(int pageno, vunits page_length) 306 { 307 assert(out != 0); 308 if (col) { 309 error("automatically outputting column before beginning next page"); 310 output(); 311 the_output->begin_page(pageno, page_length); 312 } 313 else 314 out->begin_page(pageno, page_length); 315 316 } 317 318 void column::flush() 319 { 320 assert(out != 0); 321 out->flush(); 322 } 323 324 int column::is_printing() 325 { 326 assert(out != 0); 327 return out->is_printing(); 328 } 329 330 vunits column::get_bottom() 331 { 332 return bottom; 333 } 334 335 void column::add_output_line(output_line *ln) 336 { 337 *tail = ln; 338 bottom += ln->distance(); 339 bottom += ln->height(); 340 ln->next = 0; 341 tail = &(*tail)->next; 342 } 343 344 void column::print_line(hunits page_offset, vunits pos, node *nd, 345 vunits before, vunits after) 346 { 347 assert(out != 0); 348 add_output_line(new node_output_line(pos - bottom, nd, page_offset, before, after)); 349 } 350 351 void column::vjustify(vunits pos, symbol typ) 352 { 353 assert(out != 0); 354 add_output_line(new vjustify_output_line(pos - bottom, typ)); 355 } 356 357 void column::transparent_char(unsigned char c) 358 { 359 assert(out != 0); 360 transparent_output_line *tl = 0; 361 if (*tail) 362 tl = (*tail)->as_transparent_output_line(); 363 if (!tl) { 364 tl = new transparent_output_line; 365 add_output_line(tl); 366 } 367 tl->append_char(c); 368 } 369 370 void column::copy_file(hunits page_offset, vunits pos, const char *filename) 371 { 372 assert(out != 0); 373 add_output_line(new copy_file_output_line(pos - bottom, filename, page_offset)); 374 } 375 376 void column::trim() 377 { 378 output_line **spp = 0; 379 for (output_line **pp = &col; *pp; pp = &(*pp)->next) 380 if ((*pp)->as_vjustify_output_line() == 0) 381 spp = 0; 382 else if (!spp) 383 spp = pp; 384 if (spp) { 385 output_line *ln = *spp; 386 *spp = 0; 387 tail = spp; 388 while (ln) { 389 output_line *tem = ln->next; 390 bottom -= ln->distance(); 391 bottom -= ln->height(); 392 delete ln; 393 ln = tem; 394 } 395 } 396 } 397 398 void column::reset() 399 { 400 bottom = V0; 401 for (output_line *ln = col; ln; ln = ln->next) { 402 bottom += ln->distance(); 403 ln->reset(); 404 bottom += ln->height(); 405 } 406 } 407 408 void column::check_bottom() 409 { 410 vunits b; 411 for (output_line *ln = col; ln; ln = ln->next) { 412 b += ln->distance(); 413 b += ln->height(); 414 } 415 assert(b == bottom); 416 } 417 418 void column::output() 419 { 420 assert(out != 0); 421 vunits vpos(V0); 422 output_line *ln = col; 423 while (ln) { 424 vpos += ln->distance(); 425 ln->output(out, vpos); 426 vpos += ln->height(); 427 output_line *tem = ln->next; 428 delete ln; 429 ln = tem; 430 } 431 tail = &col; 432 bottom = V0; 433 col = 0; 434 the_output = out; 435 out = 0; 436 } 437 438 vunits column::get_last_extra_space() 439 { 440 if (!col) 441 return V0; 442 for (output_line *p = col; p->next; p = p->next) 443 ; 444 return p->extra_space(); 445 } 446 447 class justification_spec { 448 vunits height; 449 symbol *type; 450 vunits *amount; 451 int n; 452 int maxn; 453 public: 454 justification_spec(vunits); 455 ~justification_spec(); 456 void append(symbol t, vunits v); 457 void justify(output_line *, vunits *bottomp) const; 458 }; 459 460 justification_spec::justification_spec(vunits h) 461 : height(h), n(0), maxn(10) 462 { 463 type = new symbol[maxn]; 464 amount = new vunits[maxn]; 465 } 466 467 justification_spec::~justification_spec() 468 { 469 a_delete type; 470 a_delete amount; 471 } 472 473 void justification_spec::append(symbol t, vunits v) 474 { 475 if (v <= V0) { 476 if (v < V0) 477 warning(WARN_RANGE, 478 "maximum space for vertical justification must not be negative"); 479 else 480 warning(WARN_RANGE, 481 "maximum space for vertical justification must not be zero"); 482 return; 483 } 484 if (n >= maxn) { 485 maxn *= 2; 486 symbol *old_type = type; 487 type = new symbol[maxn]; 488 int i; 489 for (i = 0; i < n; i++) 490 type[i] = old_type[i]; 491 a_delete old_type; 492 vunits *old_amount = amount; 493 amount = new vunits[maxn]; 494 for (i = 0; i < n; i++) 495 amount[i] = old_amount[i]; 496 a_delete old_amount; 497 } 498 assert(n < maxn); 499 type[n] = t; 500 amount[n] = v; 501 n++; 502 } 503 504 void justification_spec::justify(output_line *col, vunits *bottomp) const 505 { 506 if (*bottomp >= height) 507 return; 508 vunits total; 509 output_line *p; 510 for (p = col; p; p = p->next) { 511 vjustify_output_line *sp = p->as_vjustify_output_line(); 512 if (sp) { 513 symbol t = sp->type(); 514 for (int i = 0; i < n; i++) { 515 if (t == type[i]) 516 total += amount[i]; 517 } 518 } 519 } 520 vunits gap = height - *bottomp; 521 for (p = col; p; p = p->next) { 522 vjustify_output_line *sp = p->as_vjustify_output_line(); 523 if (sp) { 524 symbol t = sp->type(); 525 for (int i = 0; i < n; i++) { 526 if (t == type[i]) { 527 if (total <= gap) { 528 sp->vary(amount[i]); 529 gap -= amount[i]; 530 } 531 else { 532 // gap < total 533 vunits v = scale(amount[i], gap, total); 534 sp->vary(v); 535 gap -= v; 536 } 537 total -= amount[i]; 538 } 539 } 540 } 541 } 542 assert(total == V0); 543 *bottomp = height - gap; 544 } 545 546 void column::justify(const justification_spec &js) 547 { 548 check_bottom(); 549 js.justify(col, &bottom); 550 check_bottom(); 551 } 552 553 void column_justify() 554 { 555 vunits height; 556 if (!the_column->is_active()) 557 error("can't justify column - column not active"); 558 else if (get_vunits(&height, 'v')) { 559 justification_spec js(height); 560 symbol nm = get_long_name(1); 561 if (!nm.is_null()) { 562 vunits v; 563 if (get_vunits(&v, 'v')) { 564 js.append(nm, v); 565 int err = 0; 566 while (has_arg()) { 567 nm = get_long_name(1); 568 if (nm.is_null()) { 569 err = 1; 570 break; 571 } 572 if (!get_vunits(&v, 'v')) { 573 err = 1; 574 break; 575 } 576 js.append(nm, v); 577 } 578 if (!err) 579 the_column->justify(js); 580 } 581 } 582 } 583 skip_line(); 584 } 585 586 void column_start() 587 { 588 if (the_column->is_active()) 589 error("can't start column - column already active"); 590 else 591 the_column->start(); 592 skip_line(); 593 } 594 595 void column_output() 596 { 597 if (!the_column->is_active()) 598 error("can't output column - column not active"); 599 else 600 the_column->output(); 601 skip_line(); 602 } 603 604 void column_trim() 605 { 606 if (!the_column->is_active()) 607 error("can't trim column - column not active"); 608 else 609 the_column->trim(); 610 skip_line(); 611 } 612 613 void column_reset() 614 { 615 if (!the_column->is_active()) 616 error("can't reset column - column not active"); 617 else 618 the_column->reset(); 619 skip_line(); 620 } 621 622 class column_bottom_reg : public reg { 623 public: 624 const char *get_string(); 625 }; 626 627 const char *column_bottom_reg::get_string() 628 { 629 return i_to_a(the_column->get_bottom().to_units()); 630 } 631 632 class column_extra_space_reg : public reg { 633 public: 634 const char *get_string(); 635 }; 636 637 const char *column_extra_space_reg::get_string() 638 { 639 return i_to_a(the_column->get_last_extra_space().to_units()); 640 } 641 642 class column_active_reg : public reg { 643 public: 644 const char *get_string(); 645 }; 646 647 const char *column_active_reg::get_string() 648 { 649 return the_column->is_active() ? "1" : "0"; 650 } 651 652 static int no_vjustify_mode = 0; 653 654 class vjustify_node : public node { 655 symbol typ; 656 public: 657 vjustify_node(symbol); 658 int reread(int *); 659 const char *type(); 660 int same(node *); 661 node *copy(); 662 }; 663 664 vjustify_node::vjustify_node(symbol t) 665 : typ(t) 666 { 667 } 668 669 node *vjustify_node::copy() 670 { 671 return new vjustify_node(typ, div_nest_level); 672 } 673 674 const char *vjustify_node::type() 675 { 676 return "vjustify_node"; 677 } 678 679 int vjustify_node::same(node *nd) 680 { 681 return typ == ((vjustify_node *)nd)->typ; 682 } 683 684 int vjustify_node::reread(int *bolp) 685 { 686 curdiv->vjustify(typ); 687 *bolp = 1; 688 return 1; 689 } 690 691 void macro_diversion::vjustify(symbol type) 692 { 693 if (!no_vjustify_mode) 694 mac->append(new vjustify_node(type)); 695 } 696 697 void top_level_diversion::vjustify(symbol type) 698 { 699 if (no_space_mode || no_vjustify_mode) 700 return; 701 assert(first_page_begun); // I'm not sure about this. 702 the_output->vjustify(vertical_position, type); 703 } 704 705 void no_vjustify() 706 { 707 skip_line(); 708 no_vjustify_mode = 1; 709 } 710 711 void restore_vjustify() 712 { 713 skip_line(); 714 no_vjustify_mode = 0; 715 } 716 717 void init_column_requests() 718 { 719 the_column = new column; 720 init_request("cols", column_start); 721 init_request("colo", column_output); 722 init_request("colj", column_justify); 723 init_request("colr", column_reset); 724 init_request("colt", column_trim); 725 init_request("nvj", no_vjustify); 726 init_request("rvj", restore_vjustify); 727 number_reg_dictionary.define(".colb", new column_bottom_reg); 728 number_reg_dictionary.define(".colx", new column_extra_space_reg); 729 number_reg_dictionary.define(".cola", new column_active_reg); 730 number_reg_dictionary.define(".nvj", 731 new constant_int_reg(&no_vjustify_mode)); 732 } 733 734 #endif /* COLUMN */ 735