div.cpp revision 114402
1// -*- C++ -*- 2/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002 3 Free Software Foundation, Inc. 4 Written by James Clark (jjc@jclark.com) 5 6This file is part of groff. 7 8groff is free software; you can redistribute it and/or modify it under 9the terms of the GNU General Public License as published by the Free 10Software Foundation; either version 2, or (at your option) any later 11version. 12 13groff is distributed in the hope that it will be useful, but WITHOUT ANY 14WARRANTY; without even the implied warranty of MERCHANTABILITY or 15FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16for more details. 17 18You should have received a copy of the GNU General Public License along 19with groff; see the file COPYING. If not, write to the Free Software 20Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ 21 22 23// diversions 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 36int exit_started = 0; // the exit process has started 37int done_end_macro = 0; // the end macro (if any) has finished 38int seen_last_page_ejector = 0; // seen the LAST_PAGE_EJECTOR cookie 39int last_page_number = 0; // if > 0, the number of the last page 40 // specified with -o 41static int began_page_in_end_macro = 0; // a new page was begun during the end macro 42 43static int last_post_line_extra_space = 0; // needed for \n(.a 44static int nl_reg_contents = -1; 45static int dl_reg_contents = 0; 46static int dn_reg_contents = 0; 47static int vertical_position_traps_flag = 1; 48static vunits truncated_space; 49static vunits needed_space; 50 51diversion::diversion(symbol s) 52: prev(0), nm(s), vertical_position(V0), high_water_mark(V0), 53 no_space_mode(0), marked_place(V0) 54{ 55} 56 57struct vertical_size { 58 vunits pre_extra, post_extra, pre, post; 59 vertical_size(vunits vs, vunits post_vs); 60}; 61 62vertical_size::vertical_size(vunits vs, vunits post_vs) 63: pre_extra(V0), post_extra(V0), pre(vs), post(post_vs) 64{ 65} 66 67void node::set_vertical_size(vertical_size *) 68{ 69} 70 71void extra_size_node::set_vertical_size(vertical_size *v) 72{ 73 if (n < V0) { 74 if (-n > v->pre_extra) 75 v->pre_extra = -n; 76 } 77 else if (n > v->post_extra) 78 v->post_extra = n; 79} 80 81void vertical_size_node::set_vertical_size(vertical_size *v) 82{ 83 if (n < V0) 84 v->pre = -n; 85 else 86 v->post = n; 87} 88 89top_level_diversion *topdiv; 90 91diversion *curdiv; 92 93void do_divert(int append, int boxing) 94{ 95 tok.skip(); 96 symbol nm = get_name(); 97 if (nm.is_null()) { 98 if (curdiv->prev) { 99 if (boxing) { 100 curenv->line = curdiv->saved_line; 101 curenv->width_total = curdiv->saved_width_total; 102 curenv->space_total = curdiv->saved_space_total; 103 curenv->saved_indent = curdiv->saved_saved_indent; 104 curenv->target_text_length = curdiv->saved_target_text_length; 105 curenv->prev_line_interrupted = curdiv->saved_prev_line_interrupted; 106 } 107 diversion *temp = curdiv; 108 curdiv = curdiv->prev; 109 delete temp; 110 } 111 else 112 warning(WARN_DI, "diversion stack underflow"); 113 } 114 else { 115 macro_diversion *md = new macro_diversion(nm, append); 116 md->prev = curdiv; 117 curdiv = md; 118 if (boxing) { 119 curdiv->saved_line = curenv->line; 120 curdiv->saved_width_total = curenv->width_total; 121 curdiv->saved_space_total = curenv->space_total; 122 curdiv->saved_saved_indent = curenv->saved_indent; 123 curdiv->saved_target_text_length = curenv->target_text_length; 124 curdiv->saved_prev_line_interrupted = curenv->prev_line_interrupted; 125 curenv->line = 0; 126 curenv->start_line(); 127 } 128 } 129 skip_line(); 130} 131 132void divert() 133{ 134 do_divert(0, 0); 135} 136 137void divert_append() 138{ 139 do_divert(1, 0); 140} 141 142void box() 143{ 144 do_divert(0, 1); 145} 146 147void box_append() 148{ 149 do_divert(1, 1); 150} 151 152void diversion::need(vunits n) 153{ 154 vunits d = distance_to_next_trap(); 155 if (d < n) { 156 truncated_space = -d; 157 needed_space = n; 158 space(d, 1); 159 } 160} 161 162macro_diversion::macro_diversion(symbol s, int append) 163: diversion(s), max_width(H0) 164{ 165#if 0 166 if (append) { 167 /* We don't allow recursive appends eg: 168 169 .da a 170 .a 171 .di 172 173 This causes an infinite loop in troff anyway. 174 This is because the user could do 175 176 .as a foo 177 178 in the diversion, and this would mess things up royally, 179 since there would be two things appending to the same 180 macro_header. 181 To make it work, we would have to copy the _contents_ 182 of the macro into which we were diverting; this doesn't 183 strike me as worthwhile. 184 However, 185 186 .di a 187 .a 188 .a 189 .di 190 191 will work and will make `a' contain two copies of what it contained 192 before; in troff, `a' would contain nothing. */ 193 request_or_macro *rm 194 = (request_or_macro *)request_dictionary.remove(s); 195 if (!rm || (mac = rm->to_macro()) == 0) 196 mac = new macro; 197 } 198 else 199 mac = new macro; 200#endif 201 // We can now catch the situation described above by comparing 202 // the length of the charlist in the macro_header with the length 203 // stored in the macro. When we detect this, we copy the contents. 204 mac = new macro; 205 if (append) { 206 request_or_macro *rm 207 = (request_or_macro *)request_dictionary.lookup(s); 208 if (rm) { 209 macro *m = rm->to_macro(); 210 if (m) 211 *mac = *m; 212 } 213 } 214} 215 216macro_diversion::~macro_diversion() 217{ 218 request_or_macro *rm = (request_or_macro *)request_dictionary.lookup(nm); 219 macro *m = rm ? rm->to_macro() : 0; 220 if (m) { 221 *m = *mac; 222 delete mac; 223 } 224 else 225 request_dictionary.define(nm, mac); 226 mac = 0; 227 dl_reg_contents = max_width.to_units(); 228 dn_reg_contents = vertical_position.to_units(); 229} 230 231vunits macro_diversion::distance_to_next_trap() 232{ 233 if (!diversion_trap.is_null() && diversion_trap_pos > vertical_position) 234 return diversion_trap_pos - vertical_position; 235 else 236 // Substract vresolution so that vunits::vunits does not overflow. 237 return vunits(INT_MAX - vresolution); 238} 239 240void macro_diversion::transparent_output(unsigned char c) 241{ 242 mac->append(c); 243} 244 245void macro_diversion::transparent_output(node *n) 246{ 247 mac->append(n); 248} 249 250void macro_diversion::output(node *nd, int retain_size, 251 vunits vs, vunits post_vs, hunits width) 252{ 253 no_space_mode = 0; 254 vertical_size v(vs, post_vs); 255 while (nd != 0) { 256 nd->set_vertical_size(&v); 257 node *temp = nd; 258 nd = nd->next; 259 if (temp->interpret(mac)) { 260 delete temp; 261 } 262 else { 263#if 1 264 temp->freeze_space(); 265#endif 266 mac->append(temp); 267 } 268 } 269 last_post_line_extra_space = v.post_extra.to_units(); 270 if (!retain_size) { 271 v.pre = vs; 272 v.post = post_vs; 273 } 274 if (width > max_width) 275 max_width = width; 276 vunits x = v.pre + v.pre_extra + v.post + v.post_extra; 277 if (vertical_position_traps_flag 278 && !diversion_trap.is_null() && diversion_trap_pos > vertical_position 279 && diversion_trap_pos <= vertical_position + x) { 280 vunits trunc = vertical_position + x - diversion_trap_pos; 281 if (trunc > v.post) 282 trunc = v.post; 283 v.post -= trunc; 284 x -= trunc; 285 truncated_space = trunc; 286 spring_trap(diversion_trap); 287 } 288 mac->append(new vertical_size_node(-v.pre)); 289 mac->append(new vertical_size_node(v.post)); 290 mac->append('\n'); 291 vertical_position += x; 292 if (vertical_position - v.post > high_water_mark) 293 high_water_mark = vertical_position - v.post; 294} 295 296void macro_diversion::space(vunits n, int) 297{ 298 if (vertical_position_traps_flag 299 && !diversion_trap.is_null() && diversion_trap_pos > vertical_position 300 && diversion_trap_pos <= vertical_position + n) { 301 truncated_space = vertical_position + n - diversion_trap_pos; 302 n = diversion_trap_pos - vertical_position; 303 spring_trap(diversion_trap); 304 } 305 else if (n + vertical_position < V0) 306 n = -vertical_position; 307 mac->append(new diverted_space_node(n)); 308 vertical_position += n; 309} 310 311void macro_diversion::copy_file(const char *filename) 312{ 313 mac->append(new diverted_copy_file_node(filename)); 314} 315 316top_level_diversion::top_level_diversion() 317: page_number(0), page_count(0), last_page_count(-1), 318 page_length(units_per_inch*11), 319 prev_page_offset(units_per_inch), page_offset(units_per_inch), 320 page_trap_list(0), have_next_page_number(0), 321 ejecting_page(0), before_first_page(1) 322{ 323} 324 325// find the next trap after pos 326 327trap *top_level_diversion::find_next_trap(vunits *next_trap_pos) 328{ 329 trap *next_trap = 0; 330 for (trap *pt = page_trap_list; pt != 0; pt = pt->next) 331 if (!pt->nm.is_null()) { 332 if (pt->position >= V0) { 333 if (pt->position > vertical_position 334 && pt->position < page_length 335 && (next_trap == 0 || pt->position < *next_trap_pos)) { 336 next_trap = pt; 337 *next_trap_pos = pt->position; 338 } 339 } 340 else { 341 vunits pos = pt->position; 342 pos += page_length; 343 if (pos > 0 && pos > vertical_position && (next_trap == 0 || pos < *next_trap_pos)) { 344 next_trap = pt; 345 *next_trap_pos = pos; 346 } 347 } 348 } 349 return next_trap; 350} 351 352vunits top_level_diversion::distance_to_next_trap() 353{ 354 vunits d; 355 if (!find_next_trap(&d)) 356 return page_length - vertical_position; 357 else 358 return d - vertical_position; 359} 360 361void top_level_diversion::output(node *nd, int retain_size, 362 vunits vs, vunits post_vs, hunits width) 363{ 364 no_space_mode = 0; 365 vunits next_trap_pos; 366 trap *next_trap = find_next_trap(&next_trap_pos); 367 if (before_first_page && begin_page()) 368 fatal("sorry, I didn't manage to begin the first page in time: use an explicit .br request"); 369 vertical_size v(vs, post_vs); 370 for (node *tem = nd; tem != 0; tem = tem->next) 371 tem->set_vertical_size(&v); 372 last_post_line_extra_space = v.post_extra.to_units(); 373 if (!retain_size) { 374 v.pre = vs; 375 v.post = post_vs; 376 } 377 vertical_position += v.pre; 378 vertical_position += v.pre_extra; 379 the_output->print_line(page_offset, vertical_position, nd, 380 v.pre + v.pre_extra, v.post_extra, width); 381 vertical_position += v.post_extra; 382 if (vertical_position > high_water_mark) 383 high_water_mark = vertical_position; 384 if (vertical_position_traps_flag && vertical_position >= page_length) 385 begin_page(); 386 else if (vertical_position_traps_flag 387 && next_trap != 0 && vertical_position >= next_trap_pos) { 388 nl_reg_contents = vertical_position.to_units(); 389 truncated_space = v.post; 390 spring_trap(next_trap->nm); 391 } 392 else if (v.post > V0) { 393 vertical_position += v.post; 394 if (vertical_position_traps_flag 395 && next_trap != 0 && vertical_position >= next_trap_pos) { 396 truncated_space = vertical_position - next_trap_pos; 397 vertical_position = next_trap_pos; 398 nl_reg_contents = vertical_position.to_units(); 399 spring_trap(next_trap->nm); 400 } 401 else if (vertical_position_traps_flag && vertical_position >= page_length) 402 begin_page(); 403 else 404 nl_reg_contents = vertical_position.to_units(); 405 } 406 else 407 nl_reg_contents = vertical_position.to_units(); 408} 409 410void top_level_diversion::transparent_output(unsigned char c) 411{ 412 if (before_first_page && begin_page()) 413 // This can only happen with the .output request. 414 fatal("sorry, I didn't manage to begin the first page in time: use an explicit .br request"); 415 const char *s = asciify(c); 416 while (*s) 417 the_output->transparent_char(*s++); 418} 419 420void top_level_diversion::transparent_output(node * /*n*/) 421{ 422 error("can't transparently output node at top level"); 423} 424 425void top_level_diversion::copy_file(const char *filename) 426{ 427 if (before_first_page && begin_page()) 428 fatal("sorry, I didn't manage to begin the first page in time: use an explicit .br request"); 429 the_output->copy_file(page_offset, vertical_position, filename); 430} 431 432void top_level_diversion::space(vunits n, int forced) 433{ 434 if (no_space_mode) { 435 if (!forced) 436 return; 437 else 438 no_space_mode = 0; 439 } 440 if (before_first_page) { 441 begin_page(n); 442 return; 443 } 444 vunits next_trap_pos; 445 trap *next_trap = find_next_trap(&next_trap_pos); 446 vunits y = vertical_position + n; 447 if (vertical_position_traps_flag && next_trap != 0 && y >= next_trap_pos) { 448 vertical_position = next_trap_pos; 449 nl_reg_contents = vertical_position.to_units(); 450 truncated_space = y - vertical_position; 451 spring_trap(next_trap->nm); 452 } 453 else if (y < V0) { 454 vertical_position = V0; 455 nl_reg_contents = vertical_position.to_units(); 456 } 457 else if (vertical_position_traps_flag && y >= page_length && n >= V0) 458 begin_page(y - page_length); 459 else { 460 vertical_position = y; 461 nl_reg_contents = vertical_position.to_units(); 462 } 463} 464 465trap::trap(symbol s, vunits n, trap *p) 466: next(p), position(n), nm(s) 467{ 468} 469 470void top_level_diversion::add_trap(symbol nm, vunits pos) 471{ 472 trap *first_free_slot = 0; 473 trap **p; 474 for (p = &page_trap_list; *p; p = &(*p)->next) { 475 if ((*p)->nm.is_null()) { 476 if (first_free_slot == 0) 477 first_free_slot = *p; 478 } 479 else if ((*p)->position == pos) { 480 (*p)->nm = nm; 481 return; 482 } 483 } 484 if (first_free_slot) { 485 first_free_slot->nm = nm; 486 first_free_slot->position = pos; 487 } 488 else 489 *p = new trap(nm, pos, 0); 490} 491 492void top_level_diversion::remove_trap(symbol nm) 493{ 494 for (trap *p = page_trap_list; p; p = p->next) 495 if (p->nm == nm) { 496 p->nm = NULL_SYMBOL; 497 return; 498 } 499} 500 501void top_level_diversion::remove_trap_at(vunits pos) 502{ 503 for (trap *p = page_trap_list; p; p = p->next) 504 if (p->position == pos) { 505 p->nm = NULL_SYMBOL; 506 return; 507 } 508} 509 510void top_level_diversion::change_trap(symbol nm, vunits pos) 511{ 512 for (trap *p = page_trap_list; p; p = p->next) 513 if (p->nm == nm) { 514 p->position = pos; 515 return; 516 } 517} 518 519void top_level_diversion::print_traps() 520{ 521 for (trap *p = page_trap_list; p; p = p->next) 522 if (p->nm.is_null()) 523 fprintf(stderr, " empty\n"); 524 else 525 fprintf(stderr, "%s\t%d\n", p->nm.contents(), p->position.to_units()); 526 fflush(stderr); 527} 528 529void end_diversions() 530{ 531 while (curdiv != topdiv) { 532 error("automatically ending diversion `%1' on exit", 533 curdiv->nm.contents()); 534 diversion *tem = curdiv; 535 curdiv = curdiv->prev; 536 delete tem; 537 } 538} 539 540void cleanup_and_exit(int exit_code) 541{ 542 if (the_output) { 543 the_output->trailer(topdiv->get_page_length()); 544 delete the_output; 545 } 546 exit(exit_code); 547} 548 549// Returns non-zero if it sprung a top-of-page trap. 550// The optional parameter is for the .trunc register. 551int top_level_diversion::begin_page(vunits n) 552{ 553 if (exit_started) { 554 if (page_count == last_page_count 555 ? curenv->is_empty() 556 : (done_end_macro && (seen_last_page_ejector || began_page_in_end_macro))) 557 cleanup_and_exit(0); 558 if (!done_end_macro) 559 began_page_in_end_macro = 1; 560 } 561 if (last_page_number > 0 && page_number == last_page_number) 562 cleanup_and_exit(0); 563 if (!the_output) 564 init_output(); 565 ++page_count; 566 if (have_next_page_number) { 567 page_number = next_page_number; 568 have_next_page_number = 0; 569 } 570 else if (before_first_page == 1) 571 page_number = 1; 572 else 573 page_number++; 574 // spring the top of page trap if there is one 575 vunits next_trap_pos; 576 vertical_position = -vresolution; 577 trap *next_trap = find_next_trap(&next_trap_pos); 578 vertical_position = V0; 579 high_water_mark = V0; 580 ejecting_page = 0; 581 // If before_first_page was 2, then the top of page transition was undone 582 // using eg .nr nl 0-1. See nl_reg::set_value. 583 if (before_first_page != 2) 584 the_output->begin_page(page_number, page_length); 585 before_first_page = 0; 586 nl_reg_contents = vertical_position.to_units(); 587 if (vertical_position_traps_flag && next_trap != 0 && next_trap_pos == V0) { 588 truncated_space = n; 589 spring_trap(next_trap->nm); 590 return 1; 591 } 592 else 593 return 0; 594} 595 596void continue_page_eject() 597{ 598 if (topdiv->get_ejecting()) { 599 if (curdiv != topdiv) 600 error("can't continue page ejection because of current diversion"); 601 else if (!vertical_position_traps_flag) 602 error("can't continue page ejection because vertical position traps disabled"); 603 else { 604 push_page_ejector(); 605 topdiv->space(topdiv->get_page_length(), 1); 606 } 607 } 608} 609 610void top_level_diversion::set_next_page_number(int n) 611{ 612 next_page_number= n; 613 have_next_page_number = 1; 614} 615 616int top_level_diversion::get_next_page_number() 617{ 618 return have_next_page_number ? next_page_number : page_number + 1; 619} 620 621void top_level_diversion::set_page_length(vunits n) 622{ 623 page_length = n; 624} 625 626diversion::~diversion() 627{ 628} 629 630void page_offset() 631{ 632 hunits n; 633 // The troff manual says that the default scaling indicator is v, 634 // but it is in fact m: v wouldn't make sense for a horizontally 635 // oriented request. 636 if (!has_arg() || !get_hunits(&n, 'm', topdiv->page_offset)) 637 n = topdiv->prev_page_offset; 638 topdiv->prev_page_offset = topdiv->page_offset; 639 topdiv->page_offset = n; 640 curenv->add_html_tag(0, ".po", n.to_units()); 641 skip_line(); 642} 643 644void page_length() 645{ 646 vunits n; 647 if (has_arg() && get_vunits(&n, 'v', topdiv->get_page_length())) 648 topdiv->set_page_length(n); 649 else 650 topdiv->set_page_length(11*units_per_inch); 651 skip_line(); 652} 653 654void when_request() 655{ 656 vunits n; 657 if (get_vunits(&n, 'v')) { 658 symbol s = get_name(); 659 if (s.is_null()) 660 topdiv->remove_trap_at(n); 661 else 662 topdiv->add_trap(s, n); 663 } 664 skip_line(); 665} 666 667void begin_page() 668{ 669 int got_arg = 0; 670 int n; 671 if (has_arg() && get_integer(&n, topdiv->get_page_number())) 672 got_arg = 1; 673 while (!tok.newline() && !tok.eof()) 674 tok.next(); 675 if (curdiv == topdiv) { 676 if (topdiv->before_first_page) { 677 if (!break_flag) { 678 if (got_arg) 679 topdiv->set_next_page_number(n); 680 if (got_arg || !topdiv->no_space_mode) 681 topdiv->begin_page(); 682 } 683 else if (topdiv->no_space_mode && !got_arg) 684 topdiv->begin_page(); 685 else { 686 /* Given this 687 688 .wh 0 x 689 .de x 690 .tm \\n% 691 .. 692 .bp 3 693 694 troff prints 695 696 1 697 3 698 699 This code makes groff do the same. */ 700 701 push_page_ejector(); 702 topdiv->begin_page(); 703 if (got_arg) 704 topdiv->set_next_page_number(n); 705 topdiv->set_ejecting(); 706 } 707 } 708 else { 709 push_page_ejector(); 710 if (break_flag) 711 curenv->do_break(); 712 if (got_arg) 713 topdiv->set_next_page_number(n); 714 if (!(topdiv->no_space_mode && !got_arg)) 715 topdiv->set_ejecting(); 716 } 717 } 718 tok.next(); 719} 720 721void no_space() 722{ 723 curdiv->no_space_mode = 1; 724 skip_line(); 725} 726 727void restore_spacing() 728{ 729 curdiv->no_space_mode = 0; 730 skip_line(); 731} 732 733/* It is necessary to generate a break before before reading the argument, 734because otherwise arguments using | will be wrong. But if we just 735generate a break as usual, then the line forced out may spring a trap 736and thus push a macro onto the input stack before we have had a chance 737to read the argument to the sp request. We resolve this dilemma by 738setting, before generating the break, a flag which will postpone the 739actual pushing of the macro associated with the trap sprung by the 740outputting of the line forced out by the break till after we have read 741the argument to the request. If the break did cause a trap to be 742sprung, then we don't actually do the space. */ 743 744void space_request() 745{ 746 postpone_traps(); 747 if (break_flag) 748 curenv->do_break(); 749 vunits n; 750 if (!has_arg() || !get_vunits(&n, 'v')) 751 n = curenv->get_vertical_spacing(); 752 while (!tok.newline() && !tok.eof()) 753 tok.next(); 754 if (!unpostpone_traps() && !curdiv->no_space_mode) 755 curdiv->space(n); 756 else 757 // The line might have had line spacing that was truncated. 758 truncated_space += n; 759 curenv->add_html_tag(1, ".sp", n.to_units()); 760 tok.next(); 761} 762 763void blank_line() 764{ 765 curenv->do_break(); 766 if (!trap_sprung_flag && !curdiv->no_space_mode) { 767 curdiv->space(curenv->get_vertical_spacing()); 768 curenv->add_html_tag(1, ".sp", 1); 769 } 770 else 771 truncated_space += curenv->get_vertical_spacing(); 772} 773 774/* need_space might spring a trap and so we must be careful that the 775BEGIN_TRAP token is not skipped over. */ 776 777void need_space() 778{ 779 vunits n; 780 if (!has_arg() || !get_vunits(&n, 'v')) 781 n = curenv->get_vertical_spacing(); 782 while (!tok.newline() && !tok.eof()) 783 tok.next(); 784 curdiv->need(n); 785 tok.next(); 786} 787 788void page_number() 789{ 790 int n; 791 792 // the ps4html register is set if we are using -Tps 793 // to generate images for html 794 reg *r = (reg *)number_reg_dictionary.lookup("ps4html"); 795 if (r == NULL) 796 if (has_arg() && get_integer(&n, topdiv->get_page_number())) 797 topdiv->set_next_page_number(n); 798 skip_line(); 799} 800 801vunits saved_space; 802 803void save_vertical_space() 804{ 805 vunits x; 806 if (!has_arg() || !get_vunits(&x, 'v')) 807 x = curenv->get_vertical_spacing(); 808 if (curdiv->distance_to_next_trap() > x) 809 curdiv->space(x, 1); 810 else 811 saved_space = x; 812 skip_line(); 813} 814 815void output_saved_vertical_space() 816{ 817 while (!tok.newline() && !tok.eof()) 818 tok.next(); 819 if (saved_space > V0) 820 curdiv->space(saved_space, 1); 821 saved_space = V0; 822 tok.next(); 823} 824 825void flush_output() 826{ 827 while (!tok.newline() && !tok.eof()) 828 tok.next(); 829 if (break_flag) 830 curenv->do_break(); 831 if (the_output) 832 the_output->flush(); 833 curenv->add_html_tag(1, ".fl"); 834 tok.next(); 835} 836 837void macro_diversion::set_diversion_trap(symbol s, vunits n) 838{ 839 diversion_trap = s; 840 diversion_trap_pos = n; 841} 842 843void macro_diversion::clear_diversion_trap() 844{ 845 diversion_trap = NULL_SYMBOL; 846} 847 848void top_level_diversion::set_diversion_trap(symbol, vunits) 849{ 850 error("can't set diversion trap when no current diversion"); 851} 852 853void top_level_diversion::clear_diversion_trap() 854{ 855 error("can't set diversion trap when no current diversion"); 856} 857 858void diversion_trap() 859{ 860 vunits n; 861 if (has_arg() && get_vunits(&n, 'v')) { 862 symbol s = get_name(); 863 if (!s.is_null()) 864 curdiv->set_diversion_trap(s, n); 865 else 866 curdiv->clear_diversion_trap(); 867 } 868 else 869 curdiv->clear_diversion_trap(); 870 skip_line(); 871} 872 873void change_trap() 874{ 875 symbol s = get_name(1); 876 if (!s.is_null()) { 877 vunits x; 878 if (has_arg() && get_vunits(&x, 'v')) 879 topdiv->change_trap(s, x); 880 else 881 topdiv->remove_trap(s); 882 } 883 skip_line(); 884} 885 886void print_traps() 887{ 888 topdiv->print_traps(); 889 skip_line(); 890} 891 892void mark() 893{ 894 symbol s = get_name(); 895 if (s.is_null()) 896 curdiv->marked_place = curdiv->get_vertical_position(); 897 else if (curdiv == topdiv) 898 set_number_reg(s, nl_reg_contents); 899 else 900 set_number_reg(s, curdiv->get_vertical_position().to_units()); 901 skip_line(); 902} 903 904// This is truly bizarre. It is documented in the SQ manual. 905 906void return_request() 907{ 908 vunits dist = curdiv->marked_place - curdiv->get_vertical_position(); 909 if (has_arg()) { 910 if (tok.ch() == '-') { 911 tok.next(); 912 vunits x; 913 if (get_vunits(&x, 'v')) 914 dist = -x; 915 } 916 else { 917 vunits x; 918 if (get_vunits(&x, 'v')) 919 dist = x >= V0 ? x - curdiv->get_vertical_position() : V0; 920 } 921 } 922 if (dist < V0) 923 curdiv->space(dist); 924 skip_line(); 925} 926 927void vertical_position_traps() 928{ 929 int n; 930 if (has_arg() && get_integer(&n)) 931 vertical_position_traps_flag = (n != 0); 932 else 933 vertical_position_traps_flag = 1; 934 skip_line(); 935} 936 937class page_offset_reg : public reg { 938public: 939 int get_value(units *); 940 const char *get_string(); 941}; 942 943int page_offset_reg::get_value(units *res) 944{ 945 *res = topdiv->get_page_offset().to_units(); 946 return 1; 947} 948 949const char *page_offset_reg::get_string() 950{ 951 return i_to_a(topdiv->get_page_offset().to_units()); 952} 953 954class page_length_reg : public reg { 955public: 956 int get_value(units *); 957 const char *get_string(); 958}; 959 960int page_length_reg::get_value(units *res) 961{ 962 *res = topdiv->get_page_length().to_units(); 963 return 1; 964} 965 966const char *page_length_reg::get_string() 967{ 968 return i_to_a(topdiv->get_page_length().to_units()); 969} 970 971class vertical_position_reg : public reg { 972public: 973 int get_value(units *); 974 const char *get_string(); 975}; 976 977int vertical_position_reg::get_value(units *res) 978{ 979 if (curdiv == topdiv && topdiv->before_first_page) 980 *res = -1; 981 else 982 *res = curdiv->get_vertical_position().to_units(); 983 return 1; 984} 985 986const char *vertical_position_reg::get_string() 987{ 988 if (curdiv == topdiv && topdiv->before_first_page) 989 return "-1"; 990 else 991 return i_to_a(curdiv->get_vertical_position().to_units()); 992} 993 994class high_water_mark_reg : public reg { 995public: 996 int get_value(units *); 997 const char *get_string(); 998}; 999 1000int high_water_mark_reg::get_value(units *res) 1001{ 1002 *res = curdiv->get_high_water_mark().to_units(); 1003 return 1; 1004} 1005 1006const char *high_water_mark_reg::get_string() 1007{ 1008 return i_to_a(curdiv->get_high_water_mark().to_units()); 1009} 1010 1011class distance_to_next_trap_reg : public reg { 1012public: 1013 int get_value(units *); 1014 const char *get_string(); 1015}; 1016 1017int distance_to_next_trap_reg::get_value(units *res) 1018{ 1019 *res = curdiv->distance_to_next_trap().to_units(); 1020 return 1; 1021} 1022 1023const char *distance_to_next_trap_reg::get_string() 1024{ 1025 return i_to_a(curdiv->distance_to_next_trap().to_units()); 1026} 1027 1028class diversion_name_reg : public reg { 1029public: 1030 const char *get_string(); 1031}; 1032 1033const char *diversion_name_reg::get_string() 1034{ 1035 return curdiv->get_diversion_name(); 1036} 1037 1038class page_number_reg : public general_reg { 1039public: 1040 page_number_reg(); 1041 int get_value(units *); 1042 void set_value(units); 1043}; 1044 1045page_number_reg::page_number_reg() 1046{ 1047} 1048 1049void page_number_reg::set_value(units n) 1050{ 1051 topdiv->set_page_number(n); 1052} 1053 1054int page_number_reg::get_value(units *res) 1055{ 1056 *res = topdiv->get_page_number(); 1057 return 1; 1058} 1059 1060class next_page_number_reg : public reg { 1061public: 1062 const char *get_string(); 1063}; 1064 1065const char *next_page_number_reg::get_string() 1066{ 1067 return i_to_a(topdiv->get_next_page_number()); 1068} 1069 1070class page_ejecting_reg : public reg { 1071public: 1072 const char *get_string(); 1073}; 1074 1075const char *page_ejecting_reg::get_string() 1076{ 1077 return i_to_a(topdiv->get_ejecting()); 1078} 1079 1080class constant_vunits_reg : public reg { 1081 vunits *p; 1082public: 1083 constant_vunits_reg(vunits *); 1084 const char *get_string(); 1085}; 1086 1087constant_vunits_reg::constant_vunits_reg(vunits *q) : p(q) 1088{ 1089} 1090 1091const char *constant_vunits_reg::get_string() 1092{ 1093 return i_to_a(p->to_units()); 1094} 1095 1096class nl_reg : public variable_reg { 1097public: 1098 nl_reg(); 1099 void set_value(units); 1100}; 1101 1102nl_reg::nl_reg() : variable_reg(&nl_reg_contents) 1103{ 1104} 1105 1106void nl_reg::set_value(units n) 1107{ 1108 variable_reg::set_value(n); 1109 // Setting nl to a negative value when the vertical position in 1110 // the top-level diversion is 0 undoes the top of page transition, 1111 // so that the header macro will be called as if the top of page 1112 // transition hasn't happened. This is used by Larry Wall's 1113 // wrapman program. Setting before_first_page to 2 rather than 1, 1114 // tells top_level_diversion::begin_page not to call 1115 // output_file::begin_page again. 1116 if (n < 0 && topdiv->get_vertical_position() == V0) 1117 topdiv->before_first_page = 2; 1118} 1119 1120class no_space_mode_reg : public reg { 1121public: 1122 int get_value(units *); 1123 const char *get_string(); 1124}; 1125 1126int no_space_mode_reg::get_value(units *val) 1127{ 1128 *val = curdiv->no_space_mode; 1129 return 1; 1130} 1131 1132const char *no_space_mode_reg::get_string() 1133{ 1134 return curdiv->no_space_mode ? "1" : "0"; 1135} 1136 1137void init_div_requests() 1138{ 1139 init_request("box", box); 1140 init_request("boxa", box_append); 1141 init_request("bp", begin_page); 1142 init_request("ch", change_trap); 1143 init_request("da", divert_append); 1144 init_request("di", divert); 1145 init_request("dt", diversion_trap); 1146 init_request("fl", flush_output); 1147 init_request("mk", mark); 1148 init_request("ne", need_space); 1149 init_request("ns", no_space); 1150 init_request("os", output_saved_vertical_space); 1151 init_request("pl", page_length); 1152 init_request("pn", page_number); 1153 init_request("po", page_offset); 1154 init_request("ptr", print_traps); 1155 init_request("rs", restore_spacing); 1156 init_request("rt", return_request); 1157 init_request("sp", space_request); 1158 init_request("sv", save_vertical_space); 1159 init_request("vpt", vertical_position_traps); 1160 init_request("wh", when_request); 1161 number_reg_dictionary.define(".a", 1162 new constant_int_reg(&last_post_line_extra_space)); 1163 number_reg_dictionary.define(".d", new vertical_position_reg); 1164 number_reg_dictionary.define(".h", new high_water_mark_reg); 1165 number_reg_dictionary.define(".ne", 1166 new constant_vunits_reg(&needed_space)); 1167 number_reg_dictionary.define(".ns", new no_space_mode_reg); 1168 number_reg_dictionary.define(".o", new page_offset_reg); 1169 number_reg_dictionary.define(".p", new page_length_reg); 1170 number_reg_dictionary.define(".pe", new page_ejecting_reg); 1171 number_reg_dictionary.define(".pn", new next_page_number_reg); 1172 number_reg_dictionary.define(".t", new distance_to_next_trap_reg); 1173 number_reg_dictionary.define(".trunc", 1174 new constant_vunits_reg(&truncated_space)); 1175 number_reg_dictionary.define(".vpt", 1176 new constant_int_reg(&vertical_position_traps_flag)); 1177 number_reg_dictionary.define(".z", new diversion_name_reg); 1178 number_reg_dictionary.define("dl", new variable_reg(&dl_reg_contents)); 1179 number_reg_dictionary.define("dn", new variable_reg(&dn_reg_contents)); 1180 number_reg_dictionary.define("nl", new nl_reg); 1181 number_reg_dictionary.define("%", new page_number_reg); 1182} 1183