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