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