env.cpp revision 114402
1// -*- C++ -*- 2/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003 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#include "troff.h" 23#include "symbol.h" 24#include "dictionary.h" 25#include "hvunits.h" 26#include "env.h" 27#include "request.h" 28#include "node.h" 29#include "token.h" 30#include "div.h" 31#include "reg.h" 32#include "charinfo.h" 33#include "macropath.h" 34#include "input.h" 35#include <math.h> 36 37symbol default_family("T"); 38 39enum { ADJUST_LEFT = 0, ADJUST_BOTH = 1, ADJUST_CENTER = 3, ADJUST_RIGHT = 5 }; 40 41enum { HYPHEN_LAST_LINE = 2, HYPHEN_LAST_CHARS = 4, HYPHEN_FIRST_CHARS = 8 }; 42 43struct env_list { 44 environment *env; 45 env_list *next; 46 env_list(environment *e, env_list *p) : env(e), next(p) {} 47}; 48 49env_list *env_stack; 50const int NENVIRONMENTS = 10; 51environment *env_table[NENVIRONMENTS]; 52dictionary env_dictionary(10); 53environment *curenv; 54static int next_line_number = 0; 55 56charinfo *field_delimiter_char; 57charinfo *padding_indicator_char; 58 59int translate_space_to_dummy = 0; 60 61class pending_output_line { 62 node *nd; 63 int no_fill; 64 vunits vs; 65 vunits post_vs; 66 hunits width; 67#ifdef WIDOW_CONTROL 68 int last_line; // Is it the last line of the paragraph? 69#endif /* WIDOW_CONTROL */ 70public: 71 pending_output_line *next; 72 73 pending_output_line(node *, int, vunits, vunits, hunits, 74 pending_output_line * = 0); 75 ~pending_output_line(); 76 int output(); 77 78#ifdef WIDOW_CONTROL 79 friend void environment::mark_last_line(); 80 friend void environment::output(node *, int, vunits, vunits, hunits); 81#endif /* WIDOW_CONTROL */ 82}; 83 84pending_output_line::pending_output_line(node *n, int nf, vunits v, vunits pv, 85 hunits w, pending_output_line *p) 86: nd(n), no_fill(nf), vs(v), post_vs(pv), width(w), 87#ifdef WIDOW_CONTROL 88 last_line(0), 89#endif /* WIDOW_CONTROL */ 90 next(p) 91{ 92} 93 94pending_output_line::~pending_output_line() 95{ 96 delete_node_list(nd); 97} 98 99int pending_output_line::output() 100{ 101 if (trap_sprung_flag) 102 return 0; 103#ifdef WIDOW_CONTROL 104 if (next && next->last_line && !no_fill) { 105 curdiv->need(vs + post_vs + vunits(vresolution)); 106 if (trap_sprung_flag) { 107 next->last_line = 0; // Try to avoid infinite loops. 108 return 0; 109 } 110 } 111#endif 112 curdiv->output(nd, no_fill, vs, post_vs, width); 113 nd = 0; 114 return 1; 115} 116 117void environment::output(node *nd, int no_fill, vunits vs, vunits post_vs, 118 hunits width) 119{ 120#ifdef WIDOW_CONTROL 121 while (pending_lines) { 122 if (widow_control && !pending_lines->no_fill && !pending_lines->next) 123 break; 124 if (!pending_lines->output()) 125 break; 126 pending_output_line *tem = pending_lines; 127 pending_lines = pending_lines->next; 128 delete tem; 129 } 130#else /* WIDOW_CONTROL */ 131 output_pending_lines(); 132#endif /* WIDOW_CONTROL */ 133 if (!trap_sprung_flag && !pending_lines 134#ifdef WIDOW_CONTROL 135 && (!widow_control || no_fill) 136#endif /* WIDOW_CONTROL */ 137 ) { 138 curdiv->output(nd, no_fill, vs, post_vs, width); 139 emitted_node = 1; 140 } else { 141 pending_output_line **p; 142 for (p = &pending_lines; *p; p = &(*p)->next) 143 ; 144 *p = new pending_output_line(nd, no_fill, vs, post_vs, width); 145 } 146} 147 148// a line from .tl goes at the head of the queue 149 150void environment::output_title(node *nd, int no_fill, vunits vs, 151 vunits post_vs, hunits width) 152{ 153 if (!trap_sprung_flag) 154 curdiv->output(nd, no_fill, vs, post_vs, width); 155 else 156 pending_lines = new pending_output_line(nd, no_fill, vs, post_vs, width, 157 pending_lines); 158} 159 160void environment::output_pending_lines() 161{ 162 while (pending_lines && pending_lines->output()) { 163 pending_output_line *tem = pending_lines; 164 pending_lines = pending_lines->next; 165 delete tem; 166 } 167} 168 169#ifdef WIDOW_CONTROL 170 171void environment::mark_last_line() 172{ 173 if (!widow_control || !pending_lines) 174 return; 175 for (pending_output_line *p = pending_lines; p->next; p = p->next) 176 ; 177 if (!p->no_fill) 178 p->last_line = 1; 179} 180 181void widow_control_request() 182{ 183 int n; 184 if (has_arg() && get_integer(&n)) 185 curenv->widow_control = n != 0; 186 else 187 curenv->widow_control = 1; 188 skip_line(); 189} 190 191#endif /* WIDOW_CONTROL */ 192 193/* font_size functions */ 194 195size_range *font_size::size_table = 0; 196int font_size::nranges = 0; 197 198extern "C" { 199 200int compare_ranges(const void *p1, const void *p2) 201{ 202 return ((size_range *)p1)->min - ((size_range *)p2)->min; 203} 204 205} 206 207void font_size::init_size_table(int *sizes) 208{ 209 nranges = 0; 210 while (sizes[nranges*2] != 0) 211 nranges++; 212 assert(nranges > 0); 213 size_table = new size_range[nranges]; 214 for (int i = 0; i < nranges; i++) { 215 size_table[i].min = sizes[i*2]; 216 size_table[i].max = sizes[i*2 + 1]; 217 } 218 qsort(size_table, nranges, sizeof(size_range), compare_ranges); 219} 220 221font_size::font_size(int sp) 222{ 223 for (int i = 0; i < nranges; i++) { 224 if (sp < size_table[i].min) { 225 if (i > 0 && size_table[i].min - sp >= sp - size_table[i - 1].max) 226 p = size_table[i - 1].max; 227 else 228 p = size_table[i].min; 229 return; 230 } 231 if (sp <= size_table[i].max) { 232 p = sp; 233 return; 234 } 235 } 236 p = size_table[nranges - 1].max; 237} 238 239int font_size::to_units() 240{ 241 return scale(p, units_per_inch, sizescale*72); 242} 243 244// we can't do this in a static constructor because various dictionaries 245// have to get initialized first 246 247void init_environments() 248{ 249 curenv = env_table[0] = new environment("0"); 250} 251 252void tab_character() 253{ 254 curenv->tab_char = get_optional_char(); 255 skip_line(); 256} 257 258void leader_character() 259{ 260 curenv->leader_char = get_optional_char(); 261 skip_line(); 262} 263 264void environment::add_char(charinfo *ci) 265{ 266 int s; 267 if (interrupted) 268 ; 269 // don't allow fields in dummy environments 270 else if (ci == field_delimiter_char && !dummy) { 271 if (current_field) 272 wrap_up_field(); 273 else 274 start_field(); 275 } 276 else if (current_field && ci == padding_indicator_char) 277 add_padding(); 278 else if (current_tab) { 279 if (tab_contents == 0) 280 tab_contents = new line_start_node; 281 if (ci != hyphen_indicator_char) 282 tab_contents = tab_contents->add_char(ci, this, &tab_width, &s); 283 else 284 tab_contents = tab_contents->add_discretionary_hyphen(); 285 } 286 else { 287 if (line == 0) 288 start_line(); 289 if (ci != hyphen_indicator_char) 290 line = line->add_char(ci, this, &width_total, &space_total); 291 else 292 line = line->add_discretionary_hyphen(); 293 } 294} 295 296node *environment::make_char_node(charinfo *ci) 297{ 298 return make_node(ci, this); 299} 300 301void environment::add_node(node *n) 302{ 303 if (n == 0) 304 return; 305 if (current_tab || current_field) 306 n->freeze_space(); 307 if (interrupted) { 308 delete n; 309 } 310 else if (current_tab) { 311 n->next = tab_contents; 312 tab_contents = n; 313 tab_width += n->width(); 314 } 315 else { 316 if (line == 0) { 317 if (discarding && n->discardable()) { 318 // XXX possibly: input_line_start -= n->width(); 319 delete n; 320 return; 321 } 322 start_line(); 323 } 324 width_total += n->width(); 325 space_total += n->nspaces(); 326 n->next = line; 327 line = n; 328 } 329} 330 331 332void environment::add_hyphen_indicator() 333{ 334 if (current_tab || interrupted || current_field 335 || hyphen_indicator_char != 0) 336 return; 337 if (line == 0) 338 start_line(); 339 line = line->add_discretionary_hyphen(); 340} 341 342int environment::get_hyphenation_flags() 343{ 344 return hyphenation_flags; 345} 346 347int environment::get_hyphen_line_max() 348{ 349 return hyphen_line_max; 350} 351 352int environment::get_hyphen_line_count() 353{ 354 return hyphen_line_count; 355} 356 357int environment::get_center_lines() 358{ 359 return center_lines; 360} 361 362int environment::get_right_justify_lines() 363{ 364 return right_justify_lines; 365} 366 367void environment::add_italic_correction() 368{ 369 if (current_tab) { 370 if (tab_contents) 371 tab_contents = tab_contents->add_italic_correction(&tab_width); 372 } 373 else if (line) 374 line = line->add_italic_correction(&width_total); 375} 376 377void environment::space_newline() 378{ 379 assert(!current_tab && !current_field); 380 if (interrupted) 381 return; 382 hunits x = H0; 383 hunits sw = env_space_width(this); 384 hunits ssw = env_sentence_space_width(this); 385 if (!translate_space_to_dummy) { 386 x = sw; 387 if (node_list_ends_sentence(line) == 1) 388 x += ssw; 389 } 390 width_list *w = new width_list(sw, ssw); 391 if (node_list_ends_sentence(line) == 1) 392 w->next = new width_list(sw, ssw); 393 if (line != 0 && line->merge_space(x, sw, ssw)) { 394 width_total += x; 395 return; 396 } 397 add_node(new word_space_node(x, get_fill_color(), w)); 398 possibly_break_line(0, spread_flag); 399 spread_flag = 0; 400} 401 402void environment::space() 403{ 404 space(env_space_width(this), env_sentence_space_width(this)); 405} 406 407void environment::space(hunits space_width, hunits sentence_space_width) 408{ 409 if (interrupted) 410 return; 411 if (current_field && padding_indicator_char == 0) { 412 add_padding(); 413 return; 414 } 415 hunits x = translate_space_to_dummy ? H0 : space_width; 416 node *p = current_tab ? tab_contents : line; 417 hunits *tp = current_tab ? &tab_width : &width_total; 418 if (p && p->nspaces() == 1 && p->width() == x 419 && node_list_ends_sentence(p->next) == 1) { 420 hunits xx = translate_space_to_dummy ? H0 : sentence_space_width; 421 if (p->merge_space(xx, space_width, sentence_space_width)) { 422 *tp += xx; 423 return; 424 } 425 } 426 if (p && p->merge_space(x, space_width, sentence_space_width)) { 427 *tp += x; 428 return; 429 } 430 add_node(new word_space_node(x, 431 get_fill_color(), 432 new width_list(space_width, 433 sentence_space_width))); 434 possibly_break_line(0, spread_flag); 435 spread_flag = 0; 436} 437 438node *do_underline_special(int); 439 440void environment::set_font(symbol nm) 441{ 442 if (interrupted) 443 return; 444 if (nm == symbol("P") || nm.is_empty()) { 445 if (family->make_definite(prev_fontno) < 0) 446 return; 447 int tem = fontno; 448 fontno = prev_fontno; 449 prev_fontno = tem; 450 } 451 else { 452 prev_fontno = fontno; 453 int n = symbol_fontno(nm); 454 if (n < 0) { 455 n = next_available_font_position(); 456 if (!mount_font(n, nm)) 457 return; 458 } 459 if (family->make_definite(n) < 0) 460 return; 461 fontno = n; 462 } 463 if (underline_spaces && fontno != prev_fontno) { 464 if (fontno == get_underline_fontno()) 465 add_node(do_underline_special(1)); 466 if (prev_fontno == get_underline_fontno()) 467 add_node(do_underline_special(0)); 468 } 469} 470 471void environment::set_font(int n) 472{ 473 if (interrupted) 474 return; 475 if (is_good_fontno(n)) { 476 prev_fontno = fontno; 477 fontno = n; 478 } 479 else 480 warning(WARN_FONT, "bad font number"); 481} 482 483void environment::set_family(symbol fam) 484{ 485 if (interrupted) 486 return; 487 if (fam.is_null() || fam.is_empty()) { 488 if (prev_family->make_definite(fontno) < 0) 489 return; 490 font_family *tem = family; 491 family = prev_family; 492 prev_family = tem; 493 } 494 else { 495 font_family *f = lookup_family(fam); 496 if (f->make_definite(fontno) < 0) 497 return; 498 prev_family = family; 499 family = f; 500 } 501} 502 503void environment::set_size(int n) 504{ 505 if (interrupted) 506 return; 507 if (n == 0) { 508 font_size temp = prev_size; 509 prev_size = size; 510 size = temp; 511 int temp2 = prev_requested_size; 512 prev_requested_size = requested_size; 513 requested_size = temp2; 514 } 515 else { 516 prev_size = size; 517 size = font_size(n); 518 prev_requested_size = requested_size; 519 requested_size = n; 520 } 521} 522 523void environment::set_char_height(int n) 524{ 525 if (interrupted) 526 return; 527 if (n == requested_size || n <= 0) 528 char_height = 0; 529 else 530 char_height = n; 531} 532 533void environment::set_char_slant(int n) 534{ 535 if (interrupted) 536 return; 537 char_slant = n; 538} 539 540color *environment::get_prev_glyph_color() 541{ 542 return prev_glyph_color; 543} 544 545color *environment::get_glyph_color() 546{ 547 return glyph_color; 548} 549 550color *environment::get_prev_fill_color() 551{ 552 return prev_fill_color; 553} 554 555color *environment::get_fill_color() 556{ 557 return fill_color; 558} 559 560void environment::set_glyph_color(color *c) 561{ 562 if (interrupted) 563 return; 564 curenv->prev_glyph_color = curenv->glyph_color; 565 curenv->glyph_color = c; 566} 567 568void environment::set_fill_color(color *c) 569{ 570 if (interrupted) 571 return; 572 curenv->prev_fill_color = curenv->fill_color; 573 curenv->fill_color = c; 574} 575 576environment::environment(symbol nm) 577: dummy(0), 578 prev_line_length((units_per_inch*13)/2), 579 line_length((units_per_inch*13)/2), 580 prev_title_length((units_per_inch*13)/2), 581 title_length((units_per_inch*13)/2), 582 prev_size(sizescale*10), 583 size(sizescale*10), 584 requested_size(sizescale*10), 585 prev_requested_size(sizescale*10), 586 char_height(0), 587 char_slant(0), 588 space_size(12), 589 sentence_space_size(12), 590 adjust_mode(ADJUST_BOTH), 591 fill(1), 592 interrupted(0), 593 prev_line_interrupted(0), 594 center_lines(0), 595 right_justify_lines(0), 596 prev_vertical_spacing(points_to_units(12)), 597 vertical_spacing(points_to_units(12)), 598 prev_post_vertical_spacing(0), 599 post_vertical_spacing(0), 600 prev_line_spacing(1), 601 line_spacing(1), 602 prev_indent(0), 603 indent(0), 604 temporary_indent(0), 605 have_temporary_indent(0), 606 underline_lines(0), 607 underline_spaces(0), 608 input_trap_count(0), 609 continued_input_trap(0), 610 line(0), 611 prev_text_length(0), 612 width_total(0), 613 space_total(0), 614 input_line_start(0), 615 tabs(units_per_inch/2, TAB_LEFT), 616 line_tabs(0), 617 current_tab(TAB_NONE), 618 leader_node(0), 619 tab_char(0), 620 leader_char(charset_table['.']), 621 current_field(0), 622 discarding(0), 623 spread_flag(0), 624 margin_character_flags(0), 625 margin_character_node(0), 626 margin_character_distance(points_to_units(10)), 627 numbering_nodes(0), 628 number_text_separation(1), 629 line_number_indent(0), 630 line_number_multiple(1), 631 no_number_count(0), 632 hyphenation_flags(1), 633 hyphen_line_count(0), 634 hyphen_line_max(-1), 635 hyphenation_space(H0), 636 hyphenation_margin(H0), 637 composite(0), 638 pending_lines(0), 639#ifdef WIDOW_CONTROL 640 widow_control(0), 641#endif /* WIDOW_CONTROL */ 642 ignore_next_eol(0), 643 emitted_node(0), 644 glyph_color(&default_color), 645 prev_glyph_color(&default_color), 646 fill_color(&default_color), 647 prev_fill_color(&default_color), 648 name(nm), 649 control_char('.'), 650 no_break_control_char('\''), 651 hyphen_indicator_char(0) 652{ 653 prev_family = family = lookup_family(default_family); 654 prev_fontno = fontno = 1; 655 if (!is_good_fontno(1)) 656 fatal("font number 1 not a valid font"); 657 if (family->make_definite(1) < 0) 658 fatal("invalid default family `%1'", default_family.contents()); 659 prev_fontno = fontno; 660} 661 662environment::environment(const environment *e) 663: dummy(1), 664 prev_line_length(e->prev_line_length), 665 line_length(e->line_length), 666 prev_title_length(e->prev_title_length), 667 title_length(e->title_length), 668 prev_size(e->prev_size), 669 size(e->size), 670 requested_size(e->requested_size), 671 prev_requested_size(e->prev_requested_size), 672 char_height(e->char_height), 673 char_slant(e->char_slant), 674 prev_fontno(e->prev_fontno), 675 fontno(e->fontno), 676 prev_family(e->prev_family), 677 family(e->family), 678 space_size(e->space_size), 679 sentence_space_size(e->sentence_space_size), 680 adjust_mode(e->adjust_mode), 681 fill(e->fill), 682 interrupted(0), 683 prev_line_interrupted(0), 684 center_lines(0), 685 right_justify_lines(0), 686 prev_vertical_spacing(e->prev_vertical_spacing), 687 vertical_spacing(e->vertical_spacing), 688 prev_post_vertical_spacing(e->prev_post_vertical_spacing), 689 post_vertical_spacing(e->post_vertical_spacing), 690 prev_line_spacing(e->prev_line_spacing), 691 line_spacing(e->line_spacing), 692 prev_indent(e->prev_indent), 693 indent(e->indent), 694 temporary_indent(0), 695 have_temporary_indent(0), 696 underline_lines(0), 697 underline_spaces(0), 698 input_trap_count(0), 699 continued_input_trap(0), 700 line(0), 701 prev_text_length(e->prev_text_length), 702 width_total(0), 703 space_total(0), 704 input_line_start(0), 705 tabs(e->tabs), 706 line_tabs(e->line_tabs), 707 current_tab(TAB_NONE), 708 leader_node(0), 709 tab_char(e->tab_char), 710 leader_char(e->leader_char), 711 current_field(0), 712 discarding(0), 713 spread_flag(0), 714 margin_character_flags(e->margin_character_flags), 715 margin_character_node(e->margin_character_node), 716 margin_character_distance(e->margin_character_distance), 717 numbering_nodes(0), 718 number_text_separation(e->number_text_separation), 719 line_number_indent(e->line_number_indent), 720 line_number_multiple(e->line_number_multiple), 721 no_number_count(e->no_number_count), 722 hyphenation_flags(e->hyphenation_flags), 723 hyphen_line_count(0), 724 hyphen_line_max(e->hyphen_line_max), 725 hyphenation_space(e->hyphenation_space), 726 hyphenation_margin(e->hyphenation_margin), 727 composite(0), 728 pending_lines(0), 729#ifdef WIDOW_CONTROL 730 widow_control(e->widow_control), 731#endif /* WIDOW_CONTROL */ 732 ignore_next_eol(0), 733 emitted_node(0), 734 glyph_color(e->glyph_color), 735 prev_glyph_color(e->prev_glyph_color), 736 fill_color(e->fill_color), 737 prev_fill_color(e->prev_fill_color), 738 name(e->name), // so that eg `.if "\n[.ev]"0"' works 739 control_char(e->control_char), 740 no_break_control_char(e->no_break_control_char), 741 hyphen_indicator_char(e->hyphen_indicator_char) 742{ 743} 744 745void environment::copy(const environment *e) 746{ 747 prev_line_length = e->prev_line_length; 748 line_length = e->line_length; 749 prev_title_length = e->prev_title_length; 750 title_length = e->title_length; 751 prev_size = e->prev_size; 752 size = e->size; 753 prev_requested_size = e->prev_requested_size; 754 requested_size = e->requested_size; 755 char_height = e->char_height; 756 char_slant = e->char_slant; 757 space_size = e->space_size; 758 sentence_space_size = e->sentence_space_size; 759 adjust_mode = e->adjust_mode; 760 fill = e->fill; 761 interrupted = 0; 762 prev_line_interrupted = 0; 763 center_lines = 0; 764 right_justify_lines = 0; 765 prev_vertical_spacing = e->prev_vertical_spacing; 766 vertical_spacing = e->vertical_spacing; 767 prev_post_vertical_spacing = e->prev_post_vertical_spacing, 768 post_vertical_spacing = e->post_vertical_spacing, 769 prev_line_spacing = e->prev_line_spacing; 770 line_spacing = e->line_spacing; 771 prev_indent = e->prev_indent; 772 indent = e->indent; 773 have_temporary_indent = 0; 774 temporary_indent = 0; 775 underline_lines = 0; 776 underline_spaces = 0; 777 input_trap_count = 0; 778 continued_input_trap = 0; 779 prev_text_length = e->prev_text_length; 780 width_total = 0; 781 space_total = 0; 782 input_line_start = 0; 783 control_char = e->control_char; 784 no_break_control_char = e->no_break_control_char; 785 hyphen_indicator_char = e->hyphen_indicator_char; 786 spread_flag = 0; 787 line = 0; 788 pending_lines = 0; 789 discarding = 0; 790 tabs = e->tabs; 791 line_tabs = e->line_tabs; 792 current_tab = TAB_NONE; 793 current_field = 0; 794 margin_character_flags = e->margin_character_flags; 795 margin_character_node = e->margin_character_node; 796 margin_character_distance = e->margin_character_distance; 797 numbering_nodes = 0; 798 number_text_separation = e->number_text_separation; 799 line_number_multiple = e->line_number_multiple; 800 line_number_indent = e->line_number_indent; 801 no_number_count = e->no_number_count; 802 tab_char = e->tab_char; 803 leader_char = e->leader_char; 804 hyphenation_flags = e->hyphenation_flags; 805 fontno = e->fontno; 806 prev_fontno = e->prev_fontno; 807 dummy = e->dummy; 808 family = e->family; 809 prev_family = e->prev_family; 810 leader_node = 0; 811#ifdef WIDOW_CONTROL 812 widow_control = e->widow_control; 813#endif /* WIDOW_CONTROL */ 814 hyphen_line_max = e->hyphen_line_max; 815 hyphen_line_count = 0; 816 hyphenation_space = e->hyphenation_space; 817 hyphenation_margin = e->hyphenation_margin; 818 composite = 0; 819 ignore_next_eol = e->ignore_next_eol; 820 emitted_node = e->emitted_node; 821 glyph_color= e->glyph_color; 822 prev_glyph_color = e->prev_glyph_color; 823 fill_color = e->fill_color; 824 prev_fill_color = e->prev_fill_color; 825} 826 827environment::~environment() 828{ 829 delete leader_node; 830 delete_node_list(line); 831 delete_node_list(numbering_nodes); 832} 833 834hunits environment::get_input_line_position() 835{ 836 hunits n; 837 if (line == 0) 838 n = -input_line_start; 839 else 840 n = width_total - input_line_start; 841 if (current_tab) 842 n += tab_width; 843 return n; 844} 845 846void environment::set_input_line_position(hunits n) 847{ 848 input_line_start = line == 0 ? -n : width_total - n; 849 if (current_tab) 850 input_line_start += tab_width; 851} 852 853hunits environment::get_line_length() 854{ 855 return line_length; 856} 857 858hunits environment::get_saved_line_length() 859{ 860 if (line) 861 return target_text_length + saved_indent; 862 else 863 return line_length; 864} 865 866vunits environment::get_vertical_spacing() 867{ 868 return vertical_spacing; 869} 870 871vunits environment::get_post_vertical_spacing() 872{ 873 return post_vertical_spacing; 874} 875 876int environment::get_line_spacing() 877{ 878 return line_spacing; 879} 880 881vunits environment::total_post_vertical_spacing() 882{ 883 vunits tem(post_vertical_spacing); 884 if (line_spacing > 1) 885 tem += (line_spacing - 1)*vertical_spacing; 886 return tem; 887} 888 889int environment::get_bold() 890{ 891 return get_bold_fontno(fontno); 892} 893 894hunits environment::get_digit_width() 895{ 896 return env_digit_width(this); 897} 898 899int environment::get_adjust_mode() 900{ 901 return adjust_mode; 902} 903 904int environment::get_fill() 905{ 906 return fill; 907} 908 909hunits environment::get_indent() 910{ 911 return indent; 912} 913 914hunits environment::get_saved_indent() 915{ 916 if (line) 917 return saved_indent; 918 else if (have_temporary_indent) 919 return temporary_indent; 920 else 921 return indent; 922} 923 924hunits environment::get_temporary_indent() 925{ 926 return temporary_indent; 927} 928 929hunits environment::get_title_length() 930{ 931 return title_length; 932} 933 934node *environment::get_prev_char() 935{ 936 for (node *n = current_tab ? tab_contents : line; n; n = n->next) { 937 node *last = n->last_char_node(); 938 if (last) 939 return last; 940 } 941 return 0; 942} 943 944hunits environment::get_prev_char_width() 945{ 946 node *last = get_prev_char(); 947 if (!last) 948 return H0; 949 return last->width(); 950} 951 952hunits environment::get_prev_char_skew() 953{ 954 node *last = get_prev_char(); 955 if (!last) 956 return H0; 957 return last->skew(); 958} 959 960vunits environment::get_prev_char_height() 961{ 962 node *last = get_prev_char(); 963 if (!last) 964 return V0; 965 vunits min, max; 966 last->vertical_extent(&min, &max); 967 return -min; 968} 969 970vunits environment::get_prev_char_depth() 971{ 972 node *last = get_prev_char(); 973 if (!last) 974 return V0; 975 vunits min, max; 976 last->vertical_extent(&min, &max); 977 return max; 978} 979 980hunits environment::get_text_length() 981{ 982 hunits n = line == 0 ? H0 : width_total; 983 if (current_tab) 984 n += tab_width; 985 return n; 986} 987 988hunits environment::get_prev_text_length() 989{ 990 return prev_text_length; 991} 992 993 994static int sb_reg_contents = 0; 995static int st_reg_contents = 0; 996static int ct_reg_contents = 0; 997static int rsb_reg_contents = 0; 998static int rst_reg_contents = 0; 999static int skw_reg_contents = 0; 1000static int ssc_reg_contents = 0; 1001 1002void environment::width_registers() 1003{ 1004 // this is used to implement \w; it sets the st, sb, ct registers 1005 vunits min = 0, max = 0, cur = 0; 1006 int character_type = 0; 1007 ssc_reg_contents = line ? line->subscript_correction().to_units() : 0; 1008 skw_reg_contents = line ? line->skew().to_units() : 0; 1009 line = reverse_node_list(line); 1010 vunits real_min = V0; 1011 vunits real_max = V0; 1012 vunits v1, v2; 1013 for (node *tem = line; tem; tem = tem->next) { 1014 tem->vertical_extent(&v1, &v2); 1015 v1 += cur; 1016 if (v1 < real_min) 1017 real_min = v1; 1018 v2 += cur; 1019 if (v2 > real_max) 1020 real_max = v2; 1021 if ((cur += tem->vertical_width()) < min) 1022 min = cur; 1023 else if (cur > max) 1024 max = cur; 1025 character_type |= tem->character_type(); 1026 } 1027 line = reverse_node_list(line); 1028 st_reg_contents = -min.to_units(); 1029 sb_reg_contents = -max.to_units(); 1030 rst_reg_contents = -real_min.to_units(); 1031 rsb_reg_contents = -real_max.to_units(); 1032 ct_reg_contents = character_type; 1033} 1034 1035node *environment::extract_output_line() 1036{ 1037 if (current_tab) 1038 wrap_up_tab(); 1039 node *n = line; 1040 line = 0; 1041 return n; 1042} 1043 1044/* environment related requests */ 1045 1046void environment_switch() 1047{ 1048 int pop = 0; // 1 means pop, 2 means pop but no error message on underflow 1049 if (curenv->is_dummy()) 1050 error("can't switch environments when current environment is dummy"); 1051 else if (!has_arg()) 1052 pop = 1; 1053 else { 1054 symbol nm; 1055 if (!tok.delimiter()) { 1056 // It looks like a number. 1057 int n; 1058 if (get_integer(&n)) { 1059 if (n >= 0 && n < NENVIRONMENTS) { 1060 env_stack = new env_list(curenv, env_stack); 1061 if (env_table[n] == 0) 1062 env_table[n] = new environment(i_to_a(n)); 1063 curenv = env_table[n]; 1064 } 1065 else 1066 nm = i_to_a(n); 1067 } 1068 else 1069 pop = 2; 1070 } 1071 else { 1072 nm = get_long_name(1); 1073 if (nm.is_null()) 1074 pop = 2; 1075 } 1076 if (!nm.is_null()) { 1077 environment *e = (environment *)env_dictionary.lookup(nm); 1078 if (!e) { 1079 e = new environment(nm); 1080 (void)env_dictionary.lookup(nm, e); 1081 } 1082 env_stack = new env_list(curenv, env_stack); 1083 curenv = e; 1084 } 1085 } 1086 if (pop) { 1087 if (env_stack == 0) { 1088 if (pop == 1) 1089 error("environment stack underflow"); 1090 } 1091 else { 1092 curenv = env_stack->env; 1093 env_list *tem = env_stack; 1094 env_stack = env_stack->next; 1095 delete tem; 1096 } 1097 } 1098 skip_line(); 1099} 1100 1101void environment_copy() 1102{ 1103 symbol nm; 1104 environment *e=0; 1105 tok.skip(); 1106 if (!tok.delimiter()) { 1107 // It looks like a number. 1108 int n; 1109 if (get_integer(&n)) { 1110 if (n >= 0 && n < NENVIRONMENTS) 1111 e = env_table[n]; 1112 else 1113 nm = i_to_a(n); 1114 } 1115 } 1116 else 1117 nm = get_long_name(1); 1118 if (!e && !nm.is_null()) 1119 e = (environment *)env_dictionary.lookup(nm); 1120 if (e == 0) { 1121 error("No environment to copy from"); 1122 return; 1123 } 1124 else 1125 curenv->copy(e); 1126 skip_line(); 1127} 1128 1129static symbol P_symbol("P"); 1130 1131void font_change() 1132{ 1133 symbol s = get_name(); 1134 int is_number = 1; 1135 if (s.is_null() || s == P_symbol) { 1136 s = P_symbol; 1137 is_number = 0; 1138 } 1139 else { 1140 for (const char *p = s.contents(); p != 0 && *p != 0; p++) 1141 if (!csdigit(*p)) { 1142 is_number = 0; 1143 break; 1144 } 1145 } 1146 if (is_number) 1147 curenv->set_font(atoi(s.contents())); 1148 else 1149 curenv->set_font(s); 1150 skip_line(); 1151} 1152 1153void family_change() 1154{ 1155 symbol s = get_name(); 1156 curenv->set_family(s); 1157 skip_line(); 1158} 1159 1160void point_size() 1161{ 1162 int n; 1163 if (has_arg() && get_number(&n, 'z', curenv->get_requested_point_size())) { 1164 if (n <= 0) 1165 n = 1; 1166 curenv->set_size(n); 1167 curenv->add_html_tag(0, ".ps", n); 1168 } 1169 else 1170 curenv->set_size(0); 1171 skip_line(); 1172} 1173 1174void override_sizes() 1175{ 1176 int n = 16; 1177 int *sizes = new int[n]; 1178 int i = 0; 1179 char *buf = read_string(); 1180 if (!buf) 1181 return; 1182 char *p = strtok(buf, " \t"); 1183 for (;;) { 1184 if (!p) 1185 break; 1186 int lower, upper; 1187 switch (sscanf(p, "%d-%d", &lower, &upper)) { 1188 case 1: 1189 upper = lower; 1190 // fall through 1191 case 2: 1192 if (lower <= upper && lower >= 0) 1193 break; 1194 // fall through 1195 default: 1196 warning(WARN_RANGE, "bad size range `%1'", p); 1197 return; 1198 } 1199 if (i + 2 > n) { 1200 int *old_sizes = sizes; 1201 sizes = new int[n*2]; 1202 memcpy(sizes, old_sizes, n*sizeof(int)); 1203 n *= 2; 1204 a_delete old_sizes; 1205 } 1206 sizes[i++] = lower; 1207 if (lower == 0) 1208 break; 1209 sizes[i++] = upper; 1210 p = strtok(0, " \t"); 1211 } 1212 font_size::init_size_table(sizes); 1213} 1214 1215void space_size() 1216{ 1217 int n; 1218 if (get_integer(&n)) { 1219 curenv->space_size = n; 1220 if (has_arg() && get_integer(&n)) 1221 curenv->sentence_space_size = n; 1222 else 1223 curenv->sentence_space_size = curenv->space_size; 1224 } 1225 skip_line(); 1226} 1227 1228void fill() 1229{ 1230 while (!tok.newline() && !tok.eof()) 1231 tok.next(); 1232 if (break_flag) 1233 curenv->do_break(); 1234 curenv->fill = 1; 1235 curenv->add_html_tag(1, ".fi"); 1236 curenv->add_html_tag(0, ".br"); 1237 tok.next(); 1238} 1239 1240void no_fill() 1241{ 1242 while (!tok.newline() && !tok.eof()) 1243 tok.next(); 1244 if (break_flag) 1245 curenv->do_break(); 1246 curenv->fill = 0; 1247 curenv->add_html_tag(1, ".nf"); 1248 curenv->add_html_tag(0, ".br"); 1249 curenv->add_html_tag(0, ".po", topdiv->get_page_offset().to_units()); 1250 tok.next(); 1251} 1252 1253void center() 1254{ 1255 int n; 1256 if (!has_arg() || !get_integer(&n)) 1257 n = 1; 1258 else if (n < 0) 1259 n = 0; 1260 while (!tok.newline() && !tok.eof()) 1261 tok.next(); 1262 if (break_flag) 1263 curenv->do_break(); 1264 curenv->right_justify_lines = 0; 1265 curenv->center_lines = n; 1266 curenv->add_html_tag(1, ".ce", n); 1267 tok.next(); 1268} 1269 1270void right_justify() 1271{ 1272 int n; 1273 if (!has_arg() || !get_integer(&n)) 1274 n = 1; 1275 else if (n < 0) 1276 n = 0; 1277 while (!tok.newline() && !tok.eof()) 1278 tok.next(); 1279 if (break_flag) 1280 curenv->do_break(); 1281 curenv->center_lines = 0; 1282 curenv->right_justify_lines = n; 1283 curenv->add_html_tag(1, ".rj", n); 1284 tok.next(); 1285} 1286 1287void line_length() 1288{ 1289 hunits temp; 1290 if (has_arg() && get_hunits(&temp, 'm', curenv->line_length)) { 1291 if (temp < H0) { 1292 warning(WARN_RANGE, "bad line length %1u", temp.to_units()); 1293 temp = H0; 1294 } 1295 } 1296 else 1297 temp = curenv->prev_line_length; 1298 curenv->prev_line_length = curenv->line_length; 1299 curenv->line_length = temp; 1300 curenv->add_html_tag(1, ".ll", temp.to_units()); 1301 skip_line(); 1302} 1303 1304void title_length() 1305{ 1306 hunits temp; 1307 if (has_arg() && get_hunits(&temp, 'm', curenv->title_length)) { 1308 if (temp < H0) { 1309 warning(WARN_RANGE, "bad title length %1u", temp.to_units()); 1310 temp = H0; 1311 } 1312 } 1313 else 1314 temp = curenv->prev_title_length; 1315 curenv->prev_title_length = curenv->title_length; 1316 curenv->title_length = temp; 1317 skip_line(); 1318} 1319 1320void vertical_spacing() 1321{ 1322 vunits temp; 1323 if (has_arg() && get_vunits(&temp, 'p', curenv->vertical_spacing)) { 1324 if (temp < V0) { 1325 warning(WARN_RANGE, "vertical spacing must not be negative"); 1326 temp = vresolution; 1327 } 1328 } 1329 else 1330 temp = curenv->prev_vertical_spacing; 1331 curenv->prev_vertical_spacing = curenv->vertical_spacing; 1332 curenv->vertical_spacing = temp; 1333 skip_line(); 1334} 1335 1336void post_vertical_spacing() 1337{ 1338 vunits temp; 1339 if (has_arg() && get_vunits(&temp, 'p', curenv->post_vertical_spacing)) { 1340 if (temp < V0) { 1341 warning(WARN_RANGE, 1342 "post vertical spacing must be greater than or equal to 0"); 1343 temp = V0; 1344 } 1345 } 1346 else 1347 temp = curenv->prev_post_vertical_spacing; 1348 curenv->prev_post_vertical_spacing = curenv->post_vertical_spacing; 1349 curenv->post_vertical_spacing = temp; 1350 skip_line(); 1351} 1352 1353void line_spacing() 1354{ 1355 int temp; 1356 if (has_arg() && get_integer(&temp)) { 1357 if (temp < 1) { 1358 warning(WARN_RANGE, "value %1 out of range: interpreted as 1", temp); 1359 temp = 1; 1360 } 1361 } 1362 else 1363 temp = curenv->prev_line_spacing; 1364 curenv->prev_line_spacing = curenv->line_spacing; 1365 curenv->line_spacing = temp; 1366 skip_line(); 1367} 1368 1369void indent() 1370{ 1371 hunits temp; 1372 if (has_arg() && get_hunits(&temp, 'm', curenv->indent)) { 1373 if (temp < H0) { 1374 warning(WARN_RANGE, "indent cannot be negative"); 1375 temp = H0; 1376 } 1377 } 1378 else 1379 temp = curenv->prev_indent; 1380 while (!tok.newline() && !tok.eof()) 1381 tok.next(); 1382 if (break_flag) 1383 curenv->do_break(); 1384 curenv->have_temporary_indent = 0; 1385 curenv->prev_indent = curenv->indent; 1386 curenv->indent = temp; 1387 if (break_flag) 1388 curenv->add_html_tag(1, ".in", temp.to_units()); 1389 tok.next(); 1390} 1391 1392void temporary_indent() 1393{ 1394 int err = 0; 1395 hunits temp; 1396 if (!get_hunits(&temp, 'm', curenv->get_indent())) 1397 err = 1; 1398 while (!tok.newline() && !tok.eof()) 1399 tok.next(); 1400 if (break_flag) 1401 curenv->do_break(); 1402 if (temp < H0) { 1403 warning(WARN_RANGE, "total indent cannot be negative"); 1404 temp = H0; 1405 } 1406 if (!err) { 1407 curenv->temporary_indent = temp; 1408 curenv->have_temporary_indent = 1; 1409 curenv->add_html_tag(1, ".ti", temp.to_units()); 1410 } 1411 tok.next(); 1412} 1413 1414node *do_underline_special(int underline_spaces) 1415{ 1416 macro m; 1417 m.append_str("x u "); 1418 m.append(underline_spaces + '0'); 1419 return new special_node(m, 1); 1420} 1421 1422void do_underline(int underline_spaces) 1423{ 1424 int n; 1425 if (!has_arg() || !get_integer(&n)) 1426 n = 1; 1427 if (n <= 0) { 1428 if (curenv->underline_lines > 0) { 1429 curenv->prev_fontno = curenv->fontno; 1430 curenv->fontno = curenv->pre_underline_fontno; 1431 if (underline_spaces) { 1432 curenv->underline_spaces = 0; 1433 curenv->add_node(do_underline_special(0)); 1434 } 1435 } 1436 curenv->underline_lines = 0; 1437 } 1438 else { 1439 curenv->underline_lines = n; 1440 curenv->pre_underline_fontno = curenv->fontno; 1441 curenv->fontno = get_underline_fontno(); 1442 if (underline_spaces) { 1443 curenv->underline_spaces = 1; 1444 curenv->add_node(do_underline_special(1)); 1445 } 1446 } 1447 skip_line(); 1448} 1449 1450void continuous_underline() 1451{ 1452 do_underline(1); 1453} 1454 1455void underline() 1456{ 1457 do_underline(0); 1458} 1459 1460void control_char() 1461{ 1462 curenv->control_char = '.'; 1463 if (has_arg()) { 1464 if (tok.ch() == 0) 1465 error("bad control character"); 1466 else 1467 curenv->control_char = tok.ch(); 1468 } 1469 skip_line(); 1470} 1471 1472void no_break_control_char() 1473{ 1474 curenv->no_break_control_char = '\''; 1475 if (has_arg()) { 1476 if (tok.ch() == 0) 1477 error("bad control character"); 1478 else 1479 curenv->no_break_control_char = tok.ch(); 1480 } 1481 skip_line(); 1482} 1483 1484void margin_character() 1485{ 1486 while (tok.space()) 1487 tok.next(); 1488 charinfo *ci = tok.get_char(); 1489 if (ci) { 1490 // Call tok.next() only after making the node so that 1491 // .mc \s+9\(br\s0 works. 1492 node *nd = curenv->make_char_node(ci); 1493 tok.next(); 1494 if (nd) { 1495 delete curenv->margin_character_node; 1496 curenv->margin_character_node = nd; 1497 curenv->margin_character_flags = (MARGIN_CHARACTER_ON 1498 |MARGIN_CHARACTER_NEXT); 1499 hunits d; 1500 if (has_arg() && get_hunits(&d, 'm')) 1501 curenv->margin_character_distance = d; 1502 } 1503 } 1504 else { 1505 check_missing_character(); 1506 curenv->margin_character_flags &= ~MARGIN_CHARACTER_ON; 1507 if (curenv->margin_character_flags == 0) { 1508 delete curenv->margin_character_node; 1509 curenv->margin_character_node = 0; 1510 } 1511 } 1512 skip_line(); 1513} 1514 1515void number_lines() 1516{ 1517 delete_node_list(curenv->numbering_nodes); 1518 curenv->numbering_nodes = 0; 1519 if (has_arg()) { 1520 node *nd = 0; 1521 for (int i = '9'; i >= '0'; i--) { 1522 node *tem = make_node(charset_table[i], curenv); 1523 if (!tem) { 1524 skip_line(); 1525 return; 1526 } 1527 tem->next = nd; 1528 nd = tem; 1529 } 1530 curenv->numbering_nodes = nd; 1531 curenv->line_number_digit_width = env_digit_width(curenv); 1532 int n; 1533 if (!tok.delimiter()) { 1534 if (get_integer(&n, next_line_number)) { 1535 next_line_number = n; 1536 if (next_line_number < 0) { 1537 warning(WARN_RANGE, "negative line number"); 1538 next_line_number = 0; 1539 } 1540 } 1541 } 1542 else 1543 while (!tok.space() && !tok.newline() && !tok.eof()) 1544 tok.next(); 1545 if (has_arg()) { 1546 if (!tok.delimiter()) { 1547 if (get_integer(&n)) { 1548 if (n <= 0) { 1549 warning(WARN_RANGE, "negative or zero line number multiple"); 1550 } 1551 else 1552 curenv->line_number_multiple = n; 1553 } 1554 } 1555 else 1556 while (!tok.space() && !tok.newline() && !tok.eof()) 1557 tok.next(); 1558 if (has_arg()) { 1559 if (!tok.delimiter()) { 1560 if (get_integer(&n)) 1561 curenv->number_text_separation = n; 1562 } 1563 else 1564 while (!tok.space() && !tok.newline() && !tok.eof()) 1565 tok.next(); 1566 if (has_arg() && !tok.delimiter() && get_integer(&n)) 1567 curenv->line_number_indent = n; 1568 } 1569 } 1570 } 1571 skip_line(); 1572} 1573 1574void no_number() 1575{ 1576 int n; 1577 if (has_arg() && get_integer(&n)) 1578 curenv->no_number_count = n > 0 ? n : 0; 1579 else 1580 curenv->no_number_count = 1; 1581 skip_line(); 1582} 1583 1584void no_hyphenate() 1585{ 1586 curenv->hyphenation_flags = 0; 1587 skip_line(); 1588} 1589 1590void hyphenate_request() 1591{ 1592 int n; 1593 if (has_arg() && get_integer(&n)) 1594 curenv->hyphenation_flags = n; 1595 else 1596 curenv->hyphenation_flags = 1; 1597 skip_line(); 1598} 1599 1600void hyphen_char() 1601{ 1602 curenv->hyphen_indicator_char = get_optional_char(); 1603 skip_line(); 1604} 1605 1606void hyphen_line_max_request() 1607{ 1608 int n; 1609 if (has_arg() && get_integer(&n)) 1610 curenv->hyphen_line_max = n; 1611 else 1612 curenv->hyphen_line_max = -1; 1613 skip_line(); 1614} 1615 1616void environment::interrupt() 1617{ 1618 if (!dummy) { 1619 add_node(new transparent_dummy_node); 1620 interrupted = 1; 1621 } 1622} 1623 1624void environment::newline() 1625{ 1626 if (underline_lines > 0) { 1627 if (--underline_lines == 0) { 1628 prev_fontno = fontno; 1629 fontno = pre_underline_fontno; 1630 if (underline_spaces) { 1631 underline_spaces = 0; 1632 add_node(do_underline_special(0)); 1633 } 1634 } 1635 } 1636 if (current_field) 1637 wrap_up_field(); 1638 if (current_tab) 1639 wrap_up_tab(); 1640 // strip trailing spaces 1641 while (line != 0 && line->discardable()) { 1642 width_total -= line->width(); 1643 space_total -= line->nspaces(); 1644 node *tem = line; 1645 line = line->next; 1646 delete tem; 1647 } 1648 node *to_be_output = 0; 1649 hunits to_be_output_width; 1650 prev_line_interrupted = 0; 1651 if (dummy) 1652 space_newline(); 1653 else if (interrupted) { 1654 interrupted = 0; 1655 // see environment::final_break 1656 prev_line_interrupted = exit_started ? 2 : 1; 1657 } 1658 else if (center_lines > 0) { 1659 --center_lines; 1660 hunits x = target_text_length - width_total; 1661 if (x > H0) 1662 saved_indent += x/2; 1663 to_be_output = line; 1664 if (is_html) { 1665 node *n = make_html_tag("eol.ce"); 1666 n->next = to_be_output; 1667 to_be_output = n; 1668 } 1669 to_be_output_width = width_total; 1670 line = 0; 1671 } 1672 else if (right_justify_lines > 0) { 1673 --right_justify_lines; 1674 hunits x = target_text_length - width_total; 1675 if (x > H0) 1676 saved_indent += x; 1677 to_be_output = line; 1678 to_be_output_width = width_total; 1679 line = 0; 1680 } 1681 else if (fill) 1682 space_newline(); 1683 else { 1684 to_be_output = line; 1685 to_be_output_width = width_total; 1686 line = 0; 1687 } 1688 input_line_start = line == 0 ? H0 : width_total; 1689 if (to_be_output) { 1690 if (is_html && !fill) { 1691 if (curdiv == topdiv) { 1692 node *n = make_html_tag("eol"); 1693 1694 n->next = to_be_output; 1695 to_be_output = n; 1696 } 1697 } 1698 output_line(to_be_output, to_be_output_width); 1699 hyphen_line_count = 0; 1700 } 1701 if (input_trap_count > 0) { 1702 if (!(continued_input_trap && prev_line_interrupted)) 1703 if (--input_trap_count == 0) 1704 spring_trap(input_trap); 1705 } 1706} 1707 1708void environment::output_line(node *n, hunits width) 1709{ 1710 prev_text_length = width; 1711 if (margin_character_flags) { 1712 hunits d = line_length + margin_character_distance - saved_indent - width; 1713 if (d > 0) { 1714 n = new hmotion_node(d, get_fill_color(), n); 1715 width += d; 1716 } 1717 margin_character_flags &= ~MARGIN_CHARACTER_NEXT; 1718 node *tem; 1719 if (!margin_character_flags) { 1720 tem = margin_character_node; 1721 margin_character_node = 0; 1722 } 1723 else 1724 tem = margin_character_node->copy(); 1725 tem->next = n; 1726 n = tem; 1727 width += tem->width(); 1728 } 1729 node *nn = 0; 1730 while (n != 0) { 1731 node *tem = n->next; 1732 n->next = nn; 1733 nn = n; 1734 n = tem; 1735 } 1736 if (!saved_indent.is_zero()) 1737 nn = new hmotion_node(saved_indent, get_fill_color(), nn); 1738 width += saved_indent; 1739 if (no_number_count > 0) 1740 --no_number_count; 1741 else if (numbering_nodes) { 1742 hunits w = (line_number_digit_width 1743 *(3+line_number_indent+number_text_separation)); 1744 if (next_line_number % line_number_multiple != 0) 1745 nn = new hmotion_node(w, get_fill_color(), nn); 1746 else { 1747 hunits x = w; 1748 nn = new hmotion_node(number_text_separation * line_number_digit_width, 1749 get_fill_color(), nn); 1750 x -= number_text_separation*line_number_digit_width; 1751 char buf[30]; 1752 sprintf(buf, "%3d", next_line_number); 1753 for (char *p = strchr(buf, '\0') - 1; p >= buf && *p != ' '; --p) { 1754 node *gn = numbering_nodes; 1755 for (int count = *p - '0'; count > 0; count--) 1756 gn = gn->next; 1757 gn = gn->copy(); 1758 x -= gn->width(); 1759 gn->next = nn; 1760 nn = gn; 1761 } 1762 nn = new hmotion_node(x, get_fill_color(), nn); 1763 } 1764 width += w; 1765 ++next_line_number; 1766 } 1767 output(nn, !fill, vertical_spacing, total_post_vertical_spacing(), width); 1768} 1769 1770void environment::start_line() 1771{ 1772 assert(line == 0); 1773 discarding = 0; 1774 line = new line_start_node; 1775 if (have_temporary_indent) { 1776 saved_indent = temporary_indent; 1777 have_temporary_indent = 0; 1778 } 1779 else 1780 saved_indent = indent; 1781 target_text_length = line_length - saved_indent; 1782 width_total = H0; 1783 space_total = 0; 1784} 1785 1786hunits environment::get_hyphenation_space() 1787{ 1788 return hyphenation_space; 1789} 1790 1791void hyphenation_space_request() 1792{ 1793 hunits n; 1794 if (get_hunits(&n, 'm')) { 1795 if (n < H0) { 1796 warning(WARN_RANGE, "hyphenation space cannot be negative"); 1797 n = H0; 1798 } 1799 curenv->hyphenation_space = n; 1800 } 1801 skip_line(); 1802} 1803 1804hunits environment::get_hyphenation_margin() 1805{ 1806 return hyphenation_margin; 1807} 1808 1809void hyphenation_margin_request() 1810{ 1811 hunits n; 1812 if (get_hunits(&n, 'm')) { 1813 if (n < H0) { 1814 warning(WARN_RANGE, "hyphenation margin cannot be negative"); 1815 n = H0; 1816 } 1817 curenv->hyphenation_margin = n; 1818 } 1819 skip_line(); 1820} 1821 1822breakpoint *environment::choose_breakpoint() 1823{ 1824 hunits x = width_total; 1825 int s = space_total; 1826 node *n = line; 1827 breakpoint *best_bp = 0; // the best breakpoint so far 1828 int best_bp_fits = 0; 1829 while (n != 0) { 1830 x -= n->width(); 1831 s -= n->nspaces(); 1832 breakpoint *bp = n->get_breakpoints(x, s); 1833 while (bp != 0) { 1834 if (bp->width <= target_text_length) { 1835 if (!bp->hyphenated) { 1836 breakpoint *tem = bp->next; 1837 bp->next = 0; 1838 while (tem != 0) { 1839 breakpoint *tem1 = tem; 1840 tem = tem->next; 1841 delete tem1; 1842 } 1843 if (best_bp_fits 1844 // Decide whether to use the hyphenated breakpoint. 1845 && (hyphen_line_max < 0 1846 // Only choose the hyphenated breakpoint if it would not 1847 // exceed the maximum number of consecutive hyphenated 1848 // lines. 1849 || hyphen_line_count + 1 <= hyphen_line_max) 1850 && !(adjust_mode == ADJUST_BOTH 1851 // Don't choose the hyphenated breakpoint if the line 1852 // can be justified by adding no more than 1853 // hyphenation_space to any word space. 1854 ? (bp->nspaces > 0 1855 && (((target_text_length - bp->width 1856 + (bp->nspaces - 1)*hresolution)/bp->nspaces) 1857 <= hyphenation_space)) 1858 // Don't choose the hyphenated breakpoint if the line 1859 // is no more than hyphenation_margin short. 1860 : target_text_length - bp->width <= hyphenation_margin)) { 1861 delete bp; 1862 return best_bp; 1863 } 1864 if (best_bp) 1865 delete best_bp; 1866 return bp; 1867 } 1868 else { 1869 if ((adjust_mode == ADJUST_BOTH 1870 ? hyphenation_space == H0 1871 : hyphenation_margin == H0) 1872 && (hyphen_line_max < 0 1873 || hyphen_line_count + 1 <= hyphen_line_max)) { 1874 // No need to consider a non-hyphenated breakpoint. 1875 if (best_bp) 1876 delete best_bp; 1877 breakpoint *tem = bp->next; 1878 bp->next = 0; 1879 while (tem != 0) { 1880 breakpoint *tem1 = tem; 1881 tem = tem->next; 1882 delete tem1; 1883 } 1884 return bp; 1885 } 1886 // It fits but it's hyphenated. 1887 if (!best_bp_fits) { 1888 if (best_bp) 1889 delete best_bp; 1890 best_bp = bp; 1891 bp = bp->next; 1892 best_bp_fits = 1; 1893 } 1894 else { 1895 breakpoint *tem = bp; 1896 bp = bp->next; 1897 delete tem; 1898 } 1899 } 1900 } 1901 else { 1902 if (best_bp) 1903 delete best_bp; 1904 best_bp = bp; 1905 bp = bp->next; 1906 } 1907 } 1908 n = n->next; 1909 } 1910 if (best_bp) { 1911 if (!best_bp_fits) 1912 output_warning(WARN_BREAK, "can't break line"); 1913 return best_bp; 1914 } 1915 return 0; 1916} 1917 1918void environment::hyphenate_line(int start_here) 1919{ 1920 assert(line != 0); 1921 hyphenation_type prev_type = line->get_hyphenation_type(); 1922 node **startp; 1923 if (start_here) 1924 startp = &line; 1925 else 1926 for (startp = &line->next; *startp != 0; startp = &(*startp)->next) { 1927 hyphenation_type this_type = (*startp)->get_hyphenation_type(); 1928 if (prev_type == HYPHEN_BOUNDARY && this_type == HYPHEN_MIDDLE) 1929 break; 1930 prev_type = this_type; 1931 } 1932 if (*startp == 0) 1933 return; 1934 node *tem = *startp; 1935 do { 1936 tem = tem->next; 1937 } while (tem != 0 && tem->get_hyphenation_type() == HYPHEN_MIDDLE); 1938 int inhibit = (tem != 0 && tem->get_hyphenation_type() == HYPHEN_INHIBIT); 1939 node *end = tem; 1940 hyphen_list *sl = 0; 1941 tem = *startp; 1942 node *forward = 0; 1943 int i = 0; 1944 while (tem != end) { 1945 sl = tem->get_hyphen_list(sl, &i); 1946 node *tem1 = tem; 1947 tem = tem->next; 1948 tem1->next = forward; 1949 forward = tem1; 1950 } 1951 if (!inhibit) { 1952 // this is for characters like hyphen and emdash 1953 int prev_code = 0; 1954 for (hyphen_list *h = sl; h; h = h->next) { 1955 h->breakable = (prev_code != 0 1956 && h->next != 0 1957 && h->next->hyphenation_code != 0); 1958 prev_code = h->hyphenation_code; 1959 } 1960 } 1961 if (hyphenation_flags != 0 1962 && !inhibit 1963 // this may not be right if we have extra space on this line 1964 && !((hyphenation_flags & HYPHEN_LAST_LINE) 1965 && (curdiv->distance_to_next_trap() 1966 <= vertical_spacing + total_post_vertical_spacing())) 1967 && i >= 4) 1968 hyphenate(sl, hyphenation_flags); 1969 while (forward != 0) { 1970 node *tem1 = forward; 1971 forward = forward->next; 1972 tem1->next = 0; 1973 tem = tem1->add_self(tem, &sl); 1974 } 1975 *startp = tem; 1976} 1977 1978static node *node_list_reverse(node *n) 1979{ 1980 node *res = 0; 1981 while (n) { 1982 node *tem = n; 1983 n = n->next; 1984 tem->next = res; 1985 res = tem; 1986 } 1987 return res; 1988} 1989 1990static void distribute_space(node *n, int nspaces, hunits desired_space, 1991 int force_reverse = 0) 1992{ 1993 static int reverse = 0; 1994 if (force_reverse || reverse) 1995 n = node_list_reverse(n); 1996 if (!force_reverse && nspaces > 0 && spread_limit >= 0 1997 && desired_space.to_units() > 0) { 1998 hunits em = curenv->get_size(); 1999 double Ems = (double)desired_space.to_units() / nspaces 2000 / (em.is_zero() ? hresolution : em.to_units()); 2001 if (Ems > spread_limit) 2002 output_warning(WARN_BREAK, "spreading %1m per space", Ems); 2003 } 2004 for (node *tem = n; tem; tem = tem->next) 2005 tem->spread_space(&nspaces, &desired_space); 2006 if (force_reverse || reverse) 2007 (void)node_list_reverse(n); 2008 if (!force_reverse) 2009 reverse = !reverse; 2010 assert(desired_space.is_zero() && nspaces == 0); 2011} 2012 2013void environment::possibly_break_line(int start_here, int forced) 2014{ 2015 if (!fill || current_tab || current_field || dummy) 2016 return; 2017 while (line != 0 2018 && (forced 2019 // When a macro follows a paragraph in fill mode, the 2020 // current line should not be empty. 2021 || (width_total - line->width()) > target_text_length)) { 2022 hyphenate_line(start_here); 2023 breakpoint *bp = choose_breakpoint(); 2024 if (bp == 0) 2025 // we'll find one eventually 2026 return; 2027 node *pre, *post; 2028 node **ndp = &line; 2029 while (*ndp != bp->nd) 2030 ndp = &(*ndp)->next; 2031 bp->nd->split(bp->index, &pre, &post); 2032 *ndp = post; 2033 hunits extra_space_width = H0; 2034 switch(adjust_mode) { 2035 case ADJUST_BOTH: 2036 if (bp->nspaces != 0) 2037 extra_space_width = target_text_length - bp->width; 2038 else if (bp->width > 0 && target_text_length > 0 2039 && target_text_length > bp->width) 2040 output_warning(WARN_BREAK, "cannot adjust line"); 2041 break; 2042 case ADJUST_CENTER: 2043 saved_indent += (target_text_length - bp->width)/2; 2044 break; 2045 case ADJUST_RIGHT: 2046 saved_indent += target_text_length - bp->width; 2047 break; 2048 } 2049 distribute_space(pre, bp->nspaces, extra_space_width); 2050 hunits output_width = bp->width + extra_space_width; 2051 input_line_start -= output_width; 2052 if (bp->hyphenated) 2053 hyphen_line_count++; 2054 else 2055 hyphen_line_count = 0; 2056 delete bp; 2057 space_total = 0; 2058 width_total = 0; 2059 node *first_non_discardable = 0; 2060 node *tem; 2061 for (tem = line; tem != 0; tem = tem->next) 2062 if (!tem->discardable()) 2063 first_non_discardable = tem; 2064 node *to_be_discarded; 2065 if (first_non_discardable) { 2066 to_be_discarded = first_non_discardable->next; 2067 first_non_discardable->next = 0; 2068 for (tem = line; tem != 0; tem = tem->next) { 2069 width_total += tem->width(); 2070 space_total += tem->nspaces(); 2071 } 2072 discarding = 0; 2073 } 2074 else { 2075 discarding = 1; 2076 to_be_discarded = line; 2077 line = 0; 2078 } 2079 // Do output_line() here so that line will be 0 iff the 2080 // the environment will be empty. 2081 output_line(pre, output_width); 2082 while (to_be_discarded != 0) { 2083 tem = to_be_discarded; 2084 to_be_discarded = to_be_discarded->next; 2085 input_line_start -= tem->width(); 2086 delete tem; 2087 } 2088 if (line != 0) { 2089 if (have_temporary_indent) { 2090 saved_indent = temporary_indent; 2091 have_temporary_indent = 0; 2092 } 2093 else 2094 saved_indent = indent; 2095 target_text_length = line_length - saved_indent; 2096 } 2097 } 2098} 2099 2100/* 2101Do the break at the end of input after the end macro (if any). 2102 2103Unix troff behaves as follows: if the last line is 2104 2105foo bar\c 2106 2107it will output foo on the current page, and bar on the next page; 2108if the last line is 2109 2110foo\c 2111 2112or 2113 2114foo bar 2115 2116everything will be output on the current page. This behaviour must be 2117considered a bug. 2118 2119The problem is that some macro packages rely on this. For example, 2120the ATK macros have an end macro that emits \c if it needs to print a 2121table of contents but doesn't do a 'bp in the end macro; instead the 2122'bp is done in the bottom of page trap. This works with Unix troff, 2123provided that the current environment is not empty at the end of the 2124input file. 2125 2126The following will make macro packages that do that sort of thing work 2127even if the current environment is empty at the end of the input file. 2128If the last input line used \c and this line occurred in the end macro, 2129then we'll force everything out on the current page, but we'll make 2130sure that the environment isn't empty so that we won't exit at the 2131bottom of this page. 2132*/ 2133 2134void environment::final_break() 2135{ 2136 if (prev_line_interrupted == 2) { 2137 do_break(); 2138 add_node(new transparent_dummy_node); 2139 } 2140 else 2141 do_break(); 2142} 2143 2144/* 2145 * add_html_tag - emits a special html-tag: to help post-grohtml understand 2146 * the key troff commands 2147 */ 2148 2149void environment::add_html_tag(int force, const char *name) 2150{ 2151 if (!force && (curdiv != topdiv)) 2152 return; 2153 2154 if (is_html) { 2155 /* 2156 * need to emit tag for post-grohtml 2157 * but we check to see whether we can emit specials 2158 */ 2159 if (curdiv == topdiv && topdiv->before_first_page) 2160 topdiv->begin_page(); 2161 macro *m = new macro; 2162 m->append_str("html-tag:"); 2163 for (const char *p = name; *p; p++) 2164 if (!invalid_input_char((unsigned char)*p)) 2165 m->append(*p); 2166 curdiv->output(new special_node(*m), 1, 0, 0, 0); 2167 if (strcmp(name, ".nf") == 0) 2168 curenv->ignore_next_eol = 1; 2169 } 2170} 2171 2172/* 2173 * add_html_tag - emits a special html-tag: to help post-grohtml understand 2174 * the key troff commands, it appends a string representation 2175 * of i. 2176 */ 2177 2178void environment::add_html_tag(int force, const char *name, int i) 2179{ 2180 if (!force && (curdiv != topdiv)) 2181 return; 2182 2183 if (is_html) { 2184 /* 2185 * need to emit tag for post-grohtml 2186 * but we check to see whether we can emit specials 2187 */ 2188 if (curdiv == topdiv && topdiv->before_first_page) 2189 topdiv->begin_page(); 2190 macro *m = new macro; 2191 m->append_str("html-tag:"); 2192 for (const char *p = name; *p; p++) 2193 if (!invalid_input_char((unsigned char)*p)) 2194 m->append(*p); 2195 m->append(' '); 2196 m->append_int(i); 2197 node *n = new special_node(*m); 2198 curdiv->output(n, 1, 0, 0, 0); 2199 } 2200} 2201 2202/* 2203 * add_html_tag_tabs - emits the tab settings for post-grohtml 2204 */ 2205 2206void environment::add_html_tag_tabs(int force) 2207{ 2208 if (!force && (curdiv != topdiv)) 2209 return; 2210 2211 if (is_html) { 2212 /* 2213 * need to emit tag for post-grohtml 2214 * but we check to see whether we can emit specials 2215 */ 2216 if (curdiv == topdiv && topdiv->before_first_page) 2217 topdiv->begin_page(); 2218 macro *m = new macro; 2219 hunits d, l; 2220 enum tab_type t; 2221 m->append_str("html-tag:.ta "); 2222 do { 2223 t = curenv->tabs.distance_to_next_tab(l, &d); 2224 l += d; 2225 switch (t) { 2226 case TAB_LEFT: 2227 m->append_str(" L "); 2228 m->append_int(l.to_units()); 2229 break; 2230 case TAB_CENTER: 2231 m->append_str(" C "); 2232 m->append_int(l.to_units()); 2233 break; 2234 case TAB_RIGHT: 2235 m->append_str(" R "); 2236 m->append_int(l.to_units()); 2237 break; 2238 case TAB_NONE: 2239 break; 2240 } 2241 } while ((t != TAB_NONE) && (l < get_line_length())); 2242 curdiv->output(new special_node(*m), 1, 0, 0, 0); 2243 } 2244} 2245 2246node *environment::make_html_tag(const char *name, int i) 2247{ 2248 if (is_html) { 2249 /* 2250 * need to emit tag for post-grohtml 2251 * but we check to see whether we can emit specials 2252 */ 2253 if (curdiv == topdiv && topdiv->before_first_page) 2254 topdiv->begin_page(); 2255 macro *m = new macro; 2256 m->append_str("html-tag:"); 2257 for (const char *p = name; *p; p++) 2258 if (!invalid_input_char((unsigned char)*p)) 2259 m->append(*p); 2260 m->append(' '); 2261 m->append_int(i); 2262 return new special_node(*m); 2263 } 2264 return 0; 2265} 2266 2267node *environment::make_html_tag(const char *name) 2268{ 2269 if (is_html) { 2270 /* 2271 * need to emit tag for post-grohtml 2272 * but we check to see whether we can emit specials 2273 */ 2274 if (curdiv == topdiv && topdiv->before_first_page) 2275 topdiv->begin_page(); 2276 macro *m = new macro; 2277 m->append_str("html-tag:"); 2278 for (const char *p = name; *p; p++) 2279 if (!invalid_input_char((unsigned char)*p)) 2280 m->append(*p); 2281 return new special_node(*m); 2282 } 2283 return 0; 2284} 2285 2286void environment::do_break(int spread) 2287{ 2288 if (curdiv == topdiv && topdiv->before_first_page) { 2289 topdiv->begin_page(); 2290 return; 2291 } 2292 if (current_tab) 2293 wrap_up_tab(); 2294 if (line) { 2295 // this is so that hyphenation works 2296 line = new space_node(H0, get_fill_color(), line); 2297 space_total++; 2298 possibly_break_line(0, spread); 2299 } 2300 while (line != 0 && line->discardable()) { 2301 width_total -= line->width(); 2302 space_total -= line->nspaces(); 2303 node *tem = line; 2304 line = line->next; 2305 delete tem; 2306 } 2307 discarding = 0; 2308 input_line_start = H0; 2309 if (line != 0) { 2310 if (fill) { 2311 switch (adjust_mode) { 2312 case ADJUST_CENTER: 2313 saved_indent += (target_text_length - width_total)/2; 2314 break; 2315 case ADJUST_RIGHT: 2316 saved_indent += target_text_length - width_total; 2317 break; 2318 } 2319 } 2320 node *tem = line; 2321 line = 0; 2322 output_line(tem, width_total); 2323 hyphen_line_count = 0; 2324 } 2325 prev_line_interrupted = 0; 2326#ifdef WIDOW_CONTROL 2327 mark_last_line(); 2328 output_pending_lines(); 2329#endif /* WIDOW_CONTROL */ 2330} 2331 2332int environment::is_empty() 2333{ 2334 return !current_tab && line == 0 && pending_lines == 0; 2335} 2336 2337void do_break_request(int spread) 2338{ 2339 while (!tok.newline() && !tok.eof()) 2340 tok.next(); 2341 if (break_flag) { 2342 curenv->do_break(spread); 2343 curenv->add_html_tag(0, ".br"); 2344 } 2345 tok.next(); 2346} 2347 2348void break_request() 2349{ 2350 do_break_request(0); 2351} 2352 2353void break_spread_request() 2354{ 2355 do_break_request(1); 2356} 2357 2358void title() 2359{ 2360 if (curdiv == topdiv && topdiv->before_first_page) { 2361 handle_initial_title(); 2362 return; 2363 } 2364 node *part[3]; 2365 hunits part_width[3]; 2366 part[0] = part[1] = part[2] = 0; 2367 environment env(curenv); 2368 environment *oldenv = curenv; 2369 curenv = &env; 2370 read_title_parts(part, part_width); 2371 curenv = oldenv; 2372 curenv->size = env.size; 2373 curenv->prev_size = env.prev_size; 2374 curenv->requested_size = env.requested_size; 2375 curenv->prev_requested_size = env.prev_requested_size; 2376 curenv->char_height = env.char_height; 2377 curenv->char_slant = env.char_slant; 2378 curenv->fontno = env.fontno; 2379 curenv->prev_fontno = env.prev_fontno; 2380 curenv->glyph_color = env.glyph_color; 2381 curenv->prev_glyph_color = env.prev_glyph_color; 2382 curenv->fill_color = env.fill_color; 2383 curenv->prev_fill_color = env.prev_fill_color; 2384 node *n = 0; 2385 node *p = part[2]; 2386 while (p != 0) { 2387 node *tem = p; 2388 p = p->next; 2389 tem->next = n; 2390 n = tem; 2391 } 2392 hunits title_length(curenv->title_length); 2393 hunits f = title_length - part_width[1]; 2394 hunits f2 = f/2; 2395 n = new hmotion_node(f2 - part_width[2], curenv->get_fill_color(), n); 2396 p = part[1]; 2397 while (p != 0) { 2398 node *tem = p; 2399 p = p->next; 2400 tem->next = n; 2401 n = tem; 2402 } 2403 n = new hmotion_node(f - f2 - part_width[0], curenv->get_fill_color(), n); 2404 p = part[0]; 2405 while (p != 0) { 2406 node *tem = p; 2407 p = p->next; 2408 tem->next = n; 2409 n = tem; 2410 } 2411 curenv->output_title(n, !curenv->fill, curenv->vertical_spacing, 2412 curenv->total_post_vertical_spacing(), title_length); 2413 curenv->hyphen_line_count = 0; 2414 tok.next(); 2415} 2416 2417void adjust() 2418{ 2419 curenv->adjust_mode |= 1; 2420 if (has_arg()) { 2421 switch (tok.ch()) { 2422 case 'l': 2423 curenv->adjust_mode = ADJUST_LEFT; 2424 break; 2425 case 'r': 2426 curenv->adjust_mode = ADJUST_RIGHT; 2427 break; 2428 case 'c': 2429 curenv->adjust_mode = ADJUST_CENTER; 2430 break; 2431 case 'b': 2432 case 'n': 2433 curenv->adjust_mode = ADJUST_BOTH; 2434 break; 2435 default: 2436 int n; 2437 if (get_integer(&n)) { 2438 if (n < 0) 2439 warning(WARN_RANGE, "negative adjustment mode"); 2440 else if (n > 5) { 2441 curenv->adjust_mode = 5; 2442 warning(WARN_RANGE, "adjustment mode `%1' out of range", n); 2443 } 2444 else 2445 curenv->adjust_mode = n; 2446 } 2447 } 2448 } 2449 skip_line(); 2450} 2451 2452void no_adjust() 2453{ 2454 curenv->adjust_mode &= ~1; 2455 skip_line(); 2456} 2457 2458void do_input_trap(int continued) 2459{ 2460 curenv->input_trap_count = 0; 2461 if (continued) 2462 curenv->continued_input_trap = 1; 2463 int n; 2464 if (has_arg() && get_integer(&n)) { 2465 if (n <= 0) 2466 warning(WARN_RANGE, 2467 "number of lines for input trap must be greater than zero"); 2468 else { 2469 symbol s = get_name(1); 2470 if (!s.is_null()) { 2471 curenv->input_trap_count = n; 2472 curenv->input_trap = s; 2473 } 2474 } 2475 } 2476 skip_line(); 2477} 2478 2479void input_trap() 2480{ 2481 do_input_trap(0); 2482} 2483 2484void input_trap_continued() 2485{ 2486 do_input_trap(1); 2487} 2488 2489/* tabs */ 2490 2491// must not be R or C or L or a legitimate part of a number expression 2492const char TAB_REPEAT_CHAR = 'T'; 2493 2494struct tab { 2495 tab *next; 2496 hunits pos; 2497 tab_type type; 2498 tab(hunits, tab_type); 2499 enum { BLOCK = 1024 }; 2500 static tab *free_list; 2501 void *operator new(size_t); 2502 void operator delete(void *); 2503}; 2504 2505tab *tab::free_list = 0; 2506 2507void *tab::operator new(size_t n) 2508{ 2509 assert(n == sizeof(tab)); 2510 if (!free_list) { 2511 free_list = (tab *)new char[sizeof(tab)*BLOCK]; 2512 for (int i = 0; i < BLOCK - 1; i++) 2513 free_list[i].next = free_list + i + 1; 2514 free_list[BLOCK-1].next = 0; 2515 } 2516 tab *p = free_list; 2517 free_list = (tab *)(free_list->next); 2518 p->next = 0; 2519 return p; 2520} 2521 2522#ifdef __GNUG__ 2523/* cfront can't cope with this. */ 2524inline 2525#endif 2526void tab::operator delete(void *p) 2527{ 2528 if (p) { 2529 ((tab *)p)->next = free_list; 2530 free_list = (tab *)p; 2531 } 2532} 2533 2534tab::tab(hunits x, tab_type t) : next(0), pos(x), type(t) 2535{ 2536} 2537 2538tab_stops::tab_stops(hunits distance, tab_type type) 2539: initial_list(0) 2540{ 2541 repeated_list = new tab(distance, type); 2542} 2543 2544tab_stops::~tab_stops() 2545{ 2546 clear(); 2547} 2548 2549tab_type tab_stops::distance_to_next_tab(hunits curpos, hunits *distance) 2550{ 2551 hunits nextpos; 2552 2553 return distance_to_next_tab(curpos, distance, &nextpos); 2554} 2555 2556tab_type tab_stops::distance_to_next_tab(hunits curpos, hunits *distance, 2557 hunits *nextpos) 2558{ 2559 hunits lastpos = 0; 2560 tab *tem; 2561 for (tem = initial_list; tem && tem->pos <= curpos; tem = tem->next) 2562 lastpos = tem->pos; 2563 if (tem) { 2564 *distance = tem->pos - curpos; 2565 *nextpos = tem->pos; 2566 return tem->type; 2567 } 2568 if (repeated_list == 0) 2569 return TAB_NONE; 2570 hunits base = lastpos; 2571 for (;;) { 2572 for (tem = repeated_list; tem && tem->pos + base <= curpos; tem = tem->next) 2573 lastpos = tem->pos; 2574 if (tem) { 2575 *distance = tem->pos + base - curpos; 2576 *nextpos = tem->pos + base; 2577 return tem->type; 2578 } 2579 assert(lastpos > 0); 2580 base += lastpos; 2581 } 2582 return TAB_NONE; 2583} 2584 2585const char *tab_stops::to_string() 2586{ 2587 static char *buf = 0; 2588 static int buf_size = 0; 2589 // figure out a maximum on the amount of space we can need 2590 int count = 0; 2591 tab *p; 2592 for (p = initial_list; p; p = p->next) 2593 ++count; 2594 for (p = repeated_list; p; p = p->next) 2595 ++count; 2596 // (10 for digits + 1 for u + 1 for 'C' or 'R') + 2 for ' &' + 1 for '\0' 2597 int need = count*12 + 3; 2598 if (buf == 0 || need > buf_size) { 2599 if (buf) 2600 a_delete buf; 2601 buf_size = need; 2602 buf = new char[buf_size]; 2603 } 2604 char *ptr = buf; 2605 for (p = initial_list; p; p = p->next) { 2606 strcpy(ptr, i_to_a(p->pos.to_units())); 2607 ptr = strchr(ptr, '\0'); 2608 *ptr++ = 'u'; 2609 *ptr = '\0'; 2610 switch (p->type) { 2611 case TAB_LEFT: 2612 break; 2613 case TAB_RIGHT: 2614 *ptr++ = 'R'; 2615 break; 2616 case TAB_CENTER: 2617 *ptr++ = 'C'; 2618 break; 2619 case TAB_NONE: 2620 default: 2621 assert(0); 2622 } 2623 } 2624 if (repeated_list) 2625 *ptr++ = TAB_REPEAT_CHAR; 2626 for (p = repeated_list; p; p = p->next) { 2627 strcpy(ptr, i_to_a(p->pos.to_units())); 2628 ptr = strchr(ptr, '\0'); 2629 *ptr++ = 'u'; 2630 *ptr = '\0'; 2631 switch (p->type) { 2632 case TAB_LEFT: 2633 break; 2634 case TAB_RIGHT: 2635 *ptr++ = 'R'; 2636 break; 2637 case TAB_CENTER: 2638 *ptr++ = 'C'; 2639 break; 2640 case TAB_NONE: 2641 default: 2642 assert(0); 2643 } 2644 } 2645 *ptr++ = '\0'; 2646 return buf; 2647} 2648 2649tab_stops::tab_stops() : initial_list(0), repeated_list(0) 2650{ 2651} 2652 2653tab_stops::tab_stops(const tab_stops &ts) 2654: initial_list(0), repeated_list(0) 2655{ 2656 tab **p = &initial_list; 2657 tab *t = ts.initial_list; 2658 while (t) { 2659 *p = new tab(t->pos, t->type); 2660 t = t->next; 2661 p = &(*p)->next; 2662 } 2663 p = &repeated_list; 2664 t = ts.repeated_list; 2665 while (t) { 2666 *p = new tab(t->pos, t->type); 2667 t = t->next; 2668 p = &(*p)->next; 2669 } 2670} 2671 2672void tab_stops::clear() 2673{ 2674 while (initial_list) { 2675 tab *tem = initial_list; 2676 initial_list = initial_list->next; 2677 delete tem; 2678 } 2679 while (repeated_list) { 2680 tab *tem = repeated_list; 2681 repeated_list = repeated_list->next; 2682 delete tem; 2683 } 2684} 2685 2686void tab_stops::add_tab(hunits pos, tab_type type, int repeated) 2687{ 2688 tab **p; 2689 for (p = repeated ? &repeated_list : &initial_list; *p; p = &(*p)->next) 2690 ; 2691 *p = new tab(pos, type); 2692} 2693 2694 2695void tab_stops::operator=(const tab_stops &ts) 2696{ 2697 clear(); 2698 tab **p = &initial_list; 2699 tab *t = ts.initial_list; 2700 while (t) { 2701 *p = new tab(t->pos, t->type); 2702 t = t->next; 2703 p = &(*p)->next; 2704 } 2705 p = &repeated_list; 2706 t = ts.repeated_list; 2707 while (t) { 2708 *p = new tab(t->pos, t->type); 2709 t = t->next; 2710 p = &(*p)->next; 2711 } 2712} 2713 2714void set_tabs() 2715{ 2716 hunits pos; 2717 hunits prev_pos = 0; 2718 int first = 1; 2719 int repeated = 0; 2720 tab_stops tabs; 2721 while (has_arg()) { 2722 if (tok.ch() == TAB_REPEAT_CHAR) { 2723 tok.next(); 2724 repeated = 1; 2725 prev_pos = 0; 2726 } 2727 if (!get_hunits(&pos, 'm', prev_pos)) 2728 break; 2729 tab_type type = TAB_LEFT; 2730 if (tok.ch() == 'C') { 2731 tok.next(); 2732 type = TAB_CENTER; 2733 } 2734 else if (tok.ch() == 'R') { 2735 tok.next(); 2736 type = TAB_RIGHT; 2737 } 2738 else if (tok.ch() == 'L') { 2739 tok.next(); 2740 } 2741 if (pos <= prev_pos && !first) 2742 warning(WARN_RANGE, 2743 "positions of tab stops must be strictly increasing"); 2744 else { 2745 tabs.add_tab(pos, type, repeated); 2746 prev_pos = pos; 2747 first = 0; 2748 } 2749 } 2750 curenv->tabs = tabs; 2751 curenv->add_html_tag_tabs(1); 2752 skip_line(); 2753} 2754 2755const char *environment::get_tabs() 2756{ 2757 return tabs.to_string(); 2758} 2759 2760tab_type environment::distance_to_next_tab(hunits *distance) 2761{ 2762 return line_tabs 2763 ? curenv->tabs.distance_to_next_tab(get_text_length(), distance) 2764 : curenv->tabs.distance_to_next_tab(get_input_line_position(), distance); 2765} 2766 2767tab_type environment::distance_to_next_tab(hunits *distance, hunits *leftpos) 2768{ 2769 return line_tabs 2770 ? curenv->tabs.distance_to_next_tab(get_text_length(), distance, leftpos) 2771 : curenv->tabs.distance_to_next_tab(get_input_line_position(), distance, 2772 leftpos); 2773} 2774 2775void field_characters() 2776{ 2777 field_delimiter_char = get_optional_char(); 2778 if (field_delimiter_char) 2779 padding_indicator_char = get_optional_char(); 2780 else 2781 padding_indicator_char = 0; 2782 skip_line(); 2783} 2784 2785void line_tabs_request() 2786{ 2787 int n; 2788 if (has_arg() && get_integer(&n)) 2789 curenv->line_tabs = n != 0; 2790 else 2791 curenv->line_tabs = 1; 2792 skip_line(); 2793} 2794 2795int environment::get_line_tabs() 2796{ 2797 return line_tabs; 2798} 2799 2800void environment::wrap_up_tab() 2801{ 2802 if (!current_tab) 2803 return; 2804 if (line == 0) 2805 start_line(); 2806 hunits tab_amount; 2807 switch (current_tab) { 2808 case TAB_RIGHT: 2809 tab_amount = tab_distance - tab_width; 2810 line = make_tab_node(tab_amount, line); 2811 break; 2812 case TAB_CENTER: 2813 tab_amount = tab_distance - tab_width/2; 2814 line = make_tab_node(tab_amount, line); 2815 break; 2816 case TAB_NONE: 2817 case TAB_LEFT: 2818 default: 2819 assert(0); 2820 } 2821 width_total += tab_amount; 2822 width_total += tab_width; 2823 if (current_field) { 2824 if (tab_precedes_field) { 2825 pre_field_width += tab_amount; 2826 tab_precedes_field = 0; 2827 } 2828 field_distance -= tab_amount; 2829 field_spaces += tab_field_spaces; 2830 } 2831 if (tab_contents != 0) { 2832 node *tem; 2833 for (tem = tab_contents; tem->next != 0; tem = tem->next) 2834 ; 2835 tem->next = line; 2836 line = tab_contents; 2837 } 2838 tab_field_spaces = 0; 2839 tab_contents = 0; 2840 tab_width = H0; 2841 tab_distance = H0; 2842 current_tab = TAB_NONE; 2843} 2844 2845node *environment::make_tab_node(hunits d, node *next) 2846{ 2847 if (leader_node != 0 && d < 0) { 2848 error("motion generated by leader cannot be negative"); 2849 delete leader_node; 2850 leader_node = 0; 2851 } 2852 if (!leader_node) 2853 return new hmotion_node(d, 1, 0, get_fill_color(), next); 2854 node *n = new hline_node(d, leader_node, next); 2855 leader_node = 0; 2856 return n; 2857} 2858 2859void environment::handle_tab(int is_leader) 2860{ 2861 hunits d; 2862 hunits abs; 2863 if (current_tab) 2864 wrap_up_tab(); 2865 charinfo *ci = is_leader ? leader_char : tab_char; 2866 delete leader_node; 2867 leader_node = ci ? make_char_node(ci) : 0; 2868 tab_type t = distance_to_next_tab(&d, &abs); 2869 switch (t) { 2870 case TAB_NONE: 2871 return; 2872 case TAB_LEFT: 2873 add_node(make_tab_node(d)); 2874 add_node(make_html_tag("tab L", abs.to_units())); 2875 return; 2876 case TAB_RIGHT: 2877 add_node(make_html_tag("tab R", abs.to_units())); 2878 break; 2879 case TAB_CENTER: 2880 add_node(make_html_tag("tab C", abs.to_units())); 2881 break; 2882 default: 2883 assert(0); 2884 } 2885 tab_width = 0; 2886 tab_distance = d; 2887 tab_contents = 0; 2888 current_tab = t; 2889 tab_field_spaces = 0; 2890} 2891 2892void environment::start_field() 2893{ 2894 assert(!current_field); 2895 hunits d; 2896 if (distance_to_next_tab(&d) != TAB_NONE) { 2897 pre_field_width = get_text_length(); 2898 field_distance = d; 2899 current_field = 1; 2900 field_spaces = 0; 2901 tab_field_spaces = 0; 2902 for (node *p = line; p; p = p->next) 2903 if (p->nspaces()) { 2904 p->freeze_space(); 2905 space_total--; 2906 } 2907 tab_precedes_field = current_tab != TAB_NONE; 2908 } 2909 else 2910 error("zero field width"); 2911} 2912 2913void environment::wrap_up_field() 2914{ 2915 if (!current_tab && field_spaces == 0) 2916 add_padding(); 2917 hunits padding = field_distance - (get_text_length() - pre_field_width); 2918 if (current_tab && tab_field_spaces != 0) { 2919 hunits tab_padding = scale(padding, 2920 tab_field_spaces, 2921 field_spaces + tab_field_spaces); 2922 padding -= tab_padding; 2923 distribute_space(tab_contents, tab_field_spaces, tab_padding, 1); 2924 tab_field_spaces = 0; 2925 tab_width += tab_padding; 2926 } 2927 if (field_spaces != 0) { 2928 distribute_space(line, field_spaces, padding, 1); 2929 width_total += padding; 2930 if (current_tab) { 2931 // the start of the tab has been moved to the right by padding, so 2932 tab_distance -= padding; 2933 if (tab_distance <= H0) { 2934 // use the next tab stop instead 2935 current_tab = tabs.distance_to_next_tab(get_input_line_position() 2936 - tab_width, 2937 &tab_distance); 2938 if (current_tab == TAB_NONE || current_tab == TAB_LEFT) { 2939 width_total += tab_width; 2940 if (current_tab == TAB_LEFT) { 2941 line = make_tab_node(tab_distance, line); 2942 width_total += tab_distance; 2943 current_tab = TAB_NONE; 2944 } 2945 if (tab_contents != 0) { 2946 node *tem; 2947 for (tem = tab_contents; tem->next != 0; tem = tem->next) 2948 ; 2949 tem->next = line; 2950 line = tab_contents; 2951 tab_contents = 0; 2952 } 2953 tab_width = H0; 2954 tab_distance = H0; 2955 } 2956 } 2957 } 2958 } 2959 current_field = 0; 2960} 2961 2962void environment::add_padding() 2963{ 2964 if (current_tab) { 2965 tab_contents = new space_node(H0, get_fill_color(), tab_contents); 2966 tab_field_spaces++; 2967 } 2968 else { 2969 if (line == 0) 2970 start_line(); 2971 line = new space_node(H0, get_fill_color(), line); 2972 field_spaces++; 2973 } 2974} 2975 2976typedef int (environment::*INT_FUNCP)(); 2977typedef vunits (environment::*VUNITS_FUNCP)(); 2978typedef hunits (environment::*HUNITS_FUNCP)(); 2979typedef const char *(environment::*STRING_FUNCP)(); 2980 2981class int_env_reg : public reg { 2982 INT_FUNCP func; 2983 public: 2984 int_env_reg(INT_FUNCP); 2985 const char *get_string(); 2986 int get_value(units *val); 2987}; 2988 2989class vunits_env_reg : public reg { 2990 VUNITS_FUNCP func; 2991 public: 2992 vunits_env_reg(VUNITS_FUNCP f); 2993 const char *get_string(); 2994 int get_value(units *val); 2995}; 2996 2997 2998class hunits_env_reg : public reg { 2999 HUNITS_FUNCP func; 3000 public: 3001 hunits_env_reg(HUNITS_FUNCP f); 3002 const char *get_string(); 3003 int get_value(units *val); 3004}; 3005 3006class string_env_reg : public reg { 3007 STRING_FUNCP func; 3008public: 3009 string_env_reg(STRING_FUNCP); 3010 const char *get_string(); 3011}; 3012 3013int_env_reg::int_env_reg(INT_FUNCP f) : func(f) 3014{ 3015} 3016 3017int int_env_reg::get_value(units *val) 3018{ 3019 *val = (curenv->*func)(); 3020 return 1; 3021} 3022 3023const char *int_env_reg::get_string() 3024{ 3025 return i_to_a((curenv->*func)()); 3026} 3027 3028vunits_env_reg::vunits_env_reg(VUNITS_FUNCP f) : func(f) 3029{ 3030} 3031 3032int vunits_env_reg::get_value(units *val) 3033{ 3034 *val = (curenv->*func)().to_units(); 3035 return 1; 3036} 3037 3038const char *vunits_env_reg::get_string() 3039{ 3040 return i_to_a((curenv->*func)().to_units()); 3041} 3042 3043hunits_env_reg::hunits_env_reg(HUNITS_FUNCP f) : func(f) 3044{ 3045} 3046 3047int hunits_env_reg::get_value(units *val) 3048{ 3049 *val = (curenv->*func)().to_units(); 3050 return 1; 3051} 3052 3053const char *hunits_env_reg::get_string() 3054{ 3055 return i_to_a((curenv->*func)().to_units()); 3056} 3057 3058string_env_reg::string_env_reg(STRING_FUNCP f) : func(f) 3059{ 3060} 3061 3062const char *string_env_reg::get_string() 3063{ 3064 return (curenv->*func)(); 3065} 3066 3067class horizontal_place_reg : public general_reg { 3068public: 3069 horizontal_place_reg(); 3070 int get_value(units *); 3071 void set_value(units); 3072}; 3073 3074horizontal_place_reg::horizontal_place_reg() 3075{ 3076} 3077 3078int horizontal_place_reg::get_value(units *res) 3079{ 3080 *res = curenv->get_input_line_position().to_units(); 3081 return 1; 3082} 3083 3084void horizontal_place_reg::set_value(units n) 3085{ 3086 curenv->set_input_line_position(hunits(n)); 3087} 3088 3089const char *environment::get_font_family_string() 3090{ 3091 return family->nm.contents(); 3092} 3093 3094const char *environment::get_font_name_string() 3095{ 3096 symbol f = get_font_name(fontno, this); 3097 return f.contents(); 3098} 3099 3100const char *environment::get_name_string() 3101{ 3102 return name.contents(); 3103} 3104 3105// Convert a quantity in scaled points to ascii decimal fraction. 3106 3107const char *sptoa(int sp) 3108{ 3109 assert(sp > 0); 3110 assert(sizescale > 0); 3111 if (sizescale == 1) 3112 return i_to_a(sp); 3113 if (sp % sizescale == 0) 3114 return i_to_a(sp/sizescale); 3115 // See if 1/sizescale is exactly representable as a decimal fraction, 3116 // ie its only prime factors are 2 and 5. 3117 int n = sizescale; 3118 int power2 = 0; 3119 while ((n & 1) == 0) { 3120 n >>= 1; 3121 power2++; 3122 } 3123 int power5 = 0; 3124 while ((n % 5) == 0) { 3125 n /= 5; 3126 power5++; 3127 } 3128 if (n == 1) { 3129 int decimal_point = power5 > power2 ? power5 : power2; 3130 if (decimal_point <= 10) { 3131 int factor = 1; 3132 int t; 3133 for (t = decimal_point - power2; --t >= 0;) 3134 factor *= 2; 3135 for (t = decimal_point - power5; --t >= 0;) 3136 factor *= 5; 3137 if (factor == 1 || sp <= INT_MAX/factor) 3138 return if_to_a(sp*factor, decimal_point); 3139 } 3140 } 3141 double s = double(sp)/double(sizescale); 3142 double factor = 10.0; 3143 double val = s; 3144 int decimal_point = 0; 3145 do { 3146 double v = ceil(s*factor); 3147 if (v > INT_MAX) 3148 break; 3149 val = v; 3150 factor *= 10.0; 3151 } while (++decimal_point < 10); 3152 return if_to_a(int(val), decimal_point); 3153} 3154 3155const char *environment::get_point_size_string() 3156{ 3157 return sptoa(curenv->get_point_size()); 3158} 3159 3160const char *environment::get_requested_point_size_string() 3161{ 3162 return sptoa(curenv->get_requested_point_size()); 3163} 3164 3165#define init_int_env_reg(name, func) \ 3166 number_reg_dictionary.define(name, new int_env_reg(&environment::func)) 3167 3168#define init_vunits_env_reg(name, func) \ 3169 number_reg_dictionary.define(name, new vunits_env_reg(&environment::func)) 3170 3171#define init_hunits_env_reg(name, func) \ 3172 number_reg_dictionary.define(name, new hunits_env_reg(&environment::func)) 3173 3174#define init_string_env_reg(name, func) \ 3175 number_reg_dictionary.define(name, new string_env_reg(&environment::func)) 3176 3177void init_env_requests() 3178{ 3179 init_request("ad", adjust); 3180 init_request("br", break_request); 3181 init_request("brp", break_spread_request); 3182 init_request("c2", no_break_control_char); 3183 init_request("cc", control_char); 3184 init_request("ce", center); 3185 init_request("cu", continuous_underline); 3186 init_request("ev", environment_switch); 3187 init_request("evc", environment_copy); 3188 init_request("fam", family_change); 3189 init_request("fc", field_characters); 3190 init_request("fi", fill); 3191 init_request("ft", font_change); 3192 init_request("hc", hyphen_char); 3193 init_request("hlm", hyphen_line_max_request); 3194 init_request("hy", hyphenate_request); 3195 init_request("hym", hyphenation_margin_request); 3196 init_request("hys", hyphenation_space_request); 3197 init_request("in", indent); 3198 init_request("it", input_trap); 3199 init_request("itc", input_trap_continued); 3200 init_request("lc", leader_character); 3201 init_request("linetabs", line_tabs_request); 3202 init_request("ll", line_length); 3203 init_request("ls", line_spacing); 3204 init_request("lt", title_length); 3205 init_request("mc", margin_character); 3206 init_request("na", no_adjust); 3207 init_request("nf", no_fill); 3208 init_request("nh", no_hyphenate); 3209 init_request("nm", number_lines); 3210 init_request("nn", no_number); 3211 init_request("ps", point_size); 3212 init_request("pvs", post_vertical_spacing); 3213 init_request("rj", right_justify); 3214 init_request("sizes", override_sizes); 3215 init_request("ss", space_size); 3216 init_request("ta", set_tabs); 3217 init_request("ti", temporary_indent); 3218 init_request("tc", tab_character); 3219 init_request("tl", title); 3220 init_request("ul", underline); 3221 init_request("vs", vertical_spacing); 3222#ifdef WIDOW_CONTROL 3223 init_request("wdc", widow_control_request); 3224#endif /* WIDOW_CONTROL */ 3225 init_int_env_reg(".b", get_bold); 3226 init_vunits_env_reg(".cdp", get_prev_char_depth); 3227 init_int_env_reg(".ce", get_center_lines); 3228 init_vunits_env_reg(".cht", get_prev_char_height); 3229 init_hunits_env_reg(".csk", get_prev_char_skew); 3230 init_string_env_reg(".ev", get_name_string); 3231 init_int_env_reg(".f", get_font); 3232 init_string_env_reg(".fam", get_font_family_string); 3233 init_string_env_reg(".fn", get_font_name_string); 3234 init_int_env_reg(".height", get_char_height); 3235 init_int_env_reg(".hlc", get_hyphen_line_count); 3236 init_int_env_reg(".hlm", get_hyphen_line_max); 3237 init_int_env_reg(".hy", get_hyphenation_flags); 3238 init_hunits_env_reg(".hym", get_hyphenation_margin); 3239 init_hunits_env_reg(".hys", get_hyphenation_space); 3240 init_hunits_env_reg(".i", get_indent); 3241 init_hunits_env_reg(".in", get_saved_indent); 3242 init_int_env_reg(".int", get_prev_line_interrupted); 3243 init_int_env_reg(".linetabs", get_line_tabs); 3244 init_hunits_env_reg(".lt", get_title_length); 3245 init_int_env_reg(".j", get_adjust_mode); 3246 init_hunits_env_reg(".k", get_text_length); 3247 init_int_env_reg(".L", get_line_spacing); 3248 init_hunits_env_reg(".l", get_line_length); 3249 init_hunits_env_reg(".ll", get_saved_line_length); 3250 init_hunits_env_reg(".n", get_prev_text_length); 3251 init_int_env_reg(".ps", get_point_size); 3252 init_int_env_reg(".psr", get_requested_point_size); 3253 init_vunits_env_reg(".pvs", get_post_vertical_spacing); 3254 init_int_env_reg(".rj", get_right_justify_lines); 3255 init_string_env_reg(".s", get_point_size_string); 3256 init_int_env_reg(".slant", get_char_slant); 3257 init_int_env_reg(".ss", get_space_size); 3258 init_int_env_reg(".sss", get_sentence_space_size); 3259 init_string_env_reg(".sr", get_requested_point_size_string); 3260 init_string_env_reg(".tabs", get_tabs); 3261 init_int_env_reg(".u", get_fill); 3262 init_vunits_env_reg(".v", get_vertical_spacing); 3263 init_hunits_env_reg(".w", get_prev_char_width); 3264 number_reg_dictionary.define("ct", new variable_reg(&ct_reg_contents)); 3265 number_reg_dictionary.define("hp", new horizontal_place_reg); 3266 number_reg_dictionary.define("ln", new variable_reg(&next_line_number)); 3267 number_reg_dictionary.define("rsb", new variable_reg(&rsb_reg_contents)); 3268 number_reg_dictionary.define("rst", new variable_reg(&rst_reg_contents)); 3269 number_reg_dictionary.define("sb", new variable_reg(&sb_reg_contents)); 3270 number_reg_dictionary.define("skw", new variable_reg(&skw_reg_contents)); 3271 number_reg_dictionary.define("ssc", new variable_reg(&ssc_reg_contents)); 3272 number_reg_dictionary.define("st", new variable_reg(&st_reg_contents)); 3273} 3274 3275// Hyphenation - TeX's hyphenation algorithm with a less fancy implementation. 3276 3277struct trie_node; 3278 3279class trie { 3280 trie_node *tp; 3281 virtual void do_match(int len, void *val) = 0; 3282 virtual void do_delete(void *) = 0; 3283 void delete_trie_node(trie_node *); 3284public: 3285 trie() : tp(0) {} 3286 virtual ~trie(); // virtual to shut up g++ 3287 void insert(const char *, int, void *); 3288 // find calls do_match for each match it finds 3289 void find(const char *pat, int patlen); 3290 void clear(); 3291}; 3292 3293class hyphen_trie : private trie { 3294 int *h; 3295 void do_match(int i, void *v); 3296 void do_delete(void *v); 3297 void insert_pattern(const char *pat, int patlen, int *num); 3298 void insert_hyphenation(dictionary *ex, const char *pat, int patlen); 3299 int hpf_getc(FILE *f); 3300public: 3301 hyphen_trie() {} 3302 ~hyphen_trie() {} 3303 void hyphenate(const char *word, int len, int *hyphens); 3304 void read_patterns_file(const char *name, int append, dictionary *ex); 3305}; 3306 3307struct hyphenation_language { 3308 symbol name; 3309 dictionary exceptions; 3310 hyphen_trie patterns; 3311 hyphenation_language(symbol nm) : name(nm), exceptions(501) {} 3312 ~hyphenation_language() { } 3313}; 3314 3315dictionary language_dictionary(5); 3316hyphenation_language *current_language = 0; 3317 3318static void set_hyphenation_language() 3319{ 3320 symbol nm = get_name(1); 3321 if (!nm.is_null()) { 3322 current_language = (hyphenation_language *)language_dictionary.lookup(nm); 3323 if (!current_language) { 3324 current_language = new hyphenation_language(nm); 3325 (void)language_dictionary.lookup(nm, (void *)current_language); 3326 } 3327 } 3328 skip_line(); 3329} 3330 3331const int WORD_MAX = 256; // we use unsigned char for offsets in 3332 // hyphenation exceptions 3333 3334static void hyphen_word() 3335{ 3336 if (!current_language) { 3337 error("no current hyphenation language"); 3338 skip_line(); 3339 return; 3340 } 3341 char buf[WORD_MAX + 1]; 3342 unsigned char pos[WORD_MAX + 2]; 3343 for (;;) { 3344 tok.skip(); 3345 if (tok.newline() || tok.eof()) 3346 break; 3347 int i = 0; 3348 int npos = 0; 3349 while (i < WORD_MAX && !tok.space() && !tok.newline() && !tok.eof()) { 3350 charinfo *ci = tok.get_char(1); 3351 if (ci == 0) { 3352 skip_line(); 3353 return; 3354 } 3355 tok.next(); 3356 if (ci->get_ascii_code() == '-') { 3357 if (i > 0 && (npos == 0 || pos[npos - 1] != i)) 3358 pos[npos++] = i; 3359 } 3360 else { 3361 int c = ci->get_hyphenation_code(); 3362 if (c == 0) 3363 break; 3364 buf[i++] = c; 3365 } 3366 } 3367 if (i > 0) { 3368 pos[npos] = 0; 3369 buf[i] = 0; 3370 unsigned char *tem = new unsigned char[npos + 1]; 3371 memcpy(tem, pos, npos + 1); 3372 tem = (unsigned char *)current_language->exceptions.lookup(symbol(buf), 3373 tem); 3374 if (tem) 3375 a_delete tem; 3376 } 3377 } 3378 skip_line(); 3379} 3380 3381struct trie_node { 3382 char c; 3383 trie_node *down; 3384 trie_node *right; 3385 void *val; 3386 trie_node(char, trie_node *); 3387}; 3388 3389trie_node::trie_node(char ch, trie_node *p) 3390: c(ch), down(0), right(p), val(0) 3391{ 3392} 3393 3394trie::~trie() 3395{ 3396 clear(); 3397} 3398 3399void trie::clear() 3400{ 3401 delete_trie_node(tp); 3402 tp = 0; 3403} 3404 3405 3406void trie::delete_trie_node(trie_node *p) 3407{ 3408 if (p) { 3409 delete_trie_node(p->down); 3410 delete_trie_node(p->right); 3411 if (p->val) 3412 do_delete(p->val); 3413 delete p; 3414 } 3415} 3416 3417void trie::insert(const char *pat, int patlen, void *val) 3418{ 3419 trie_node **p = &tp; 3420 assert(patlen > 0 && pat != 0); 3421 for (;;) { 3422 while (*p != 0 && (*p)->c < pat[0]) 3423 p = &((*p)->right); 3424 if (*p == 0 || (*p)->c != pat[0]) 3425 *p = new trie_node(pat[0], *p); 3426 if (--patlen == 0) { 3427 (*p)->val = val; 3428 break; 3429 } 3430 ++pat; 3431 p = &((*p)->down); 3432 } 3433} 3434 3435void trie::find(const char *pat, int patlen) 3436{ 3437 trie_node *p = tp; 3438 for (int i = 0; p != 0 && i < patlen; i++) { 3439 while (p != 0 && p->c < pat[i]) 3440 p = p->right; 3441 if (p != 0 && p->c == pat[i]) { 3442 if (p->val != 0) 3443 do_match(i+1, p->val); 3444 p = p->down; 3445 } 3446 else 3447 break; 3448 } 3449} 3450 3451struct operation { 3452 operation *next; 3453 short distance; 3454 short num; 3455 operation(int, int, operation *); 3456}; 3457 3458operation::operation(int i, int j, operation *op) 3459: next(op), distance(j), num(i) 3460{ 3461} 3462 3463void hyphen_trie::insert_pattern(const char *pat, int patlen, int *num) 3464{ 3465 operation *op = 0; 3466 for (int i = 0; i < patlen+1; i++) 3467 if (num[i] != 0) 3468 op = new operation(num[i], patlen - i, op); 3469 insert(pat, patlen, op); 3470} 3471 3472void hyphen_trie::insert_hyphenation(dictionary *ex, const char *pat, 3473 int patlen) 3474{ 3475 char buf[WORD_MAX + 1]; 3476 unsigned char pos[WORD_MAX + 2]; 3477 int i = 0, j = 0; 3478 int npos = 0; 3479 while (j < patlen) { 3480 unsigned char c = pat[j++]; 3481 if (c == '-') { 3482 if (i > 0 && (npos == 0 || pos[npos - 1] != i)) 3483 pos[npos++] = i; 3484 } 3485 else 3486 buf[i++] = hpf_code_table[c]; 3487 } 3488 if (i > 0) { 3489 pos[npos] = 0; 3490 buf[i] = 0; 3491 unsigned char *tem = new unsigned char[npos + 1]; 3492 memcpy(tem, pos, npos + 1); 3493 tem = (unsigned char *)ex->lookup(symbol(buf), tem); 3494 if (tem) 3495 a_delete tem; 3496 } 3497} 3498 3499void hyphen_trie::hyphenate(const char *word, int len, int *hyphens) 3500{ 3501 int j; 3502 for (j = 0; j < len + 1; j++) 3503 hyphens[j] = 0; 3504 for (j = 0; j < len - 1; j++) { 3505 h = hyphens + j; 3506 find(word + j, len - j); 3507 } 3508} 3509 3510inline int max(int m, int n) 3511{ 3512 return m > n ? m : n; 3513} 3514 3515void hyphen_trie::do_match(int i, void *v) 3516{ 3517 operation *op = (operation *)v; 3518 while (op != 0) { 3519 h[i - op->distance] = max(h[i - op->distance], op->num); 3520 op = op->next; 3521 } 3522} 3523 3524void hyphen_trie::do_delete(void *v) 3525{ 3526 operation *op = (operation *)v; 3527 while (op) { 3528 operation *tem = op; 3529 op = tem->next; 3530 delete tem; 3531 } 3532} 3533 3534/* We use very simple rules to parse TeX's hyphenation patterns. 3535 3536 . `%' starts a comment even if preceded by `\'. 3537 3538 . No support for digraphs and like `\$'. 3539 3540 . `^^xx' (`x' is 0-9 or a-f), and `^^x' (character code of `x' in the 3541 range 0-127) are recognized; other use of `^' causes an error. 3542 3543 . No macro expansion. 3544 3545 . We check for the expression `\patterns{...}' (possibly with 3546 whitespace before and after the braces). Everything between the 3547 braces is taken as hyphenation patterns. Consequently, `{' and `}' 3548 are not allowed in patterns. 3549 3550 . Similarly, `\hyphenation{...}' gives a list of hyphenation 3551 exceptions. 3552 3553 . `\endinput' is recognized also. 3554 3555 . For backwards compatibility, if `\patterns' is missing, the 3556 whole file is treated as a list of hyphenation patterns (only 3557 recognizing `%' as the start of a comment. 3558 3559*/ 3560 3561int hyphen_trie::hpf_getc(FILE *f) 3562{ 3563 int c = getc(f); 3564 int c1; 3565 int cc = 0; 3566 if (c != '^') 3567 return c; 3568 c = getc(f); 3569 if (c != '^') 3570 goto fail; 3571 c = getc(f); 3572 c1 = getc(f); 3573 if (((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')) 3574 && ((c1 >= '0' && c1 <= '9') || (c1 >= 'a' && c1 <= 'f'))) { 3575 if (c >= '0' && c <= '9') 3576 c -= '0'; 3577 else 3578 c = c - 'a' + 10; 3579 if (c1 >= '0' && c1 <= '9') 3580 c1 -= '0'; 3581 else 3582 c1 = c1 - 'a' + 10; 3583 cc = c * 16 + c1; 3584 } 3585 else { 3586 ungetc(c1, f); 3587 if (c >= 0 && c <= 63) 3588 cc = c + 64; 3589 else if (c >= 64 && c <= 127) 3590 cc = c - 64; 3591 else 3592 goto fail; 3593 } 3594 return cc; 3595fail: 3596 error("invalid ^, ^^x, or ^^xx character in hyphenation patterns file"); 3597 return c; 3598} 3599 3600void hyphen_trie::read_patterns_file(const char *name, int append, 3601 dictionary *ex) 3602{ 3603 if (!append) 3604 clear(); 3605 char buf[WORD_MAX]; 3606 for (int i = 0; i < WORD_MAX; i++) 3607 buf[i] = 0; 3608 int num[WORD_MAX+1]; 3609 errno = 0; 3610 char *path = 0; 3611 FILE *fp = mac_path->open_file(name, &path); 3612 if (fp == 0) { 3613 error("can't find hyphenation patterns file `%1'", name); 3614 return; 3615 } 3616 int c = hpf_getc(fp); 3617 int have_patterns = 0; // we've seen \patterns 3618 int final_pattern = 0; // 1 if we have a trailing closing brace 3619 int have_hyphenation = 0; // we've seen \hyphenation 3620 int final_hyphenation = 0; // 1 if we have a trailing closing brace 3621 int have_keyword = 0; // we've seen either \patterns or \hyphenation 3622 int traditional = 0; // don't handle \patterns 3623 for (;;) { 3624 for (;;) { 3625 if (c == '%') { // skip comments 3626 do { 3627 c = getc(fp); 3628 } while (c != EOF && c != '\n'); 3629 } 3630 if (c == EOF || !csspace(c)) 3631 break; 3632 c = hpf_getc(fp); 3633 } 3634 if (c == EOF) { 3635 if (have_keyword || traditional) // we are done 3636 break; 3637 else { // rescan file in `traditional' mode 3638 rewind(fp); 3639 traditional = 1; 3640 c = hpf_getc(fp); 3641 continue; 3642 } 3643 } 3644 int i = 0; 3645 num[0] = 0; 3646 if (!(c == '{' || c == '}')) { // skip braces at line start 3647 do { // scan patterns 3648 if (csdigit(c)) 3649 num[i] = c - '0'; 3650 else { 3651 buf[i++] = c; 3652 num[i] = 0; 3653 } 3654 c = hpf_getc(fp); 3655 } while (i < WORD_MAX && c != EOF && !csspace(c) 3656 && c != '%' && c != '{' && c != '}'); 3657 } 3658 if (!traditional) { 3659 if (i >= 9 && !strncmp(buf + i - 9, "\\patterns", 9)) { 3660 while (csspace(c)) 3661 c = hpf_getc(fp); 3662 if (c == '{') { 3663 if (have_patterns || have_hyphenation) 3664 error("`{' not allowed inside of \\patterns or \\hyphenation"); 3665 else { 3666 have_patterns = 1; 3667 have_keyword = 1; 3668 } 3669 c = hpf_getc(fp); 3670 continue; 3671 } 3672 } 3673 else if (i >= 12 && !strncmp(buf + i - 12, "\\hyphenation", 12)) { 3674 while (csspace(c)) 3675 c = hpf_getc(fp); 3676 if (c == '{') { 3677 if (have_patterns || have_hyphenation) 3678 error("`{' not allowed inside of \\patterns or \\hyphenation"); 3679 else { 3680 have_hyphenation = 1; 3681 have_keyword = 1; 3682 } 3683 c = hpf_getc(fp); 3684 continue; 3685 } 3686 } 3687 else if (strstr(buf, "\\endinput")) { 3688 if (have_patterns || have_hyphenation) 3689 error("found \\endinput inside of %1 group", 3690 have_patterns ? "\\patterns" : "\\hyphenation"); 3691 break; 3692 } 3693 else if (c == '}') { 3694 if (have_patterns) { 3695 have_patterns = 0; 3696 if (i > 0) 3697 final_pattern = 1; 3698 } 3699 else if (have_hyphenation) { 3700 have_hyphenation = 0; 3701 if (i > 0) 3702 final_hyphenation = 1; 3703 } 3704 c = hpf_getc(fp); 3705 } 3706 else if (c == '{') // skipped if not starting \patterns 3707 c = hpf_getc(fp); // or \hyphenation 3708 } 3709 if (i > 0) { 3710 if (have_patterns || final_pattern || traditional) { 3711 for (int j = 0; j < i; j++) 3712 buf[j] = hpf_code_table[(unsigned char)buf[j]]; 3713 insert_pattern(buf, i, num); 3714 final_pattern = 0; 3715 } 3716 else if (have_hyphenation || final_hyphenation) { 3717 insert_hyphenation(ex, buf, i); 3718 final_hyphenation = 0; 3719 } 3720 } 3721 } 3722 fclose(fp); 3723 a_delete path; 3724 return; 3725} 3726 3727void hyphenate(hyphen_list *h, unsigned flags) 3728{ 3729 if (!current_language) 3730 return; 3731 while (h) { 3732 while (h && h->hyphenation_code == 0) 3733 h = h->next; 3734 int len = 0; 3735 char hbuf[WORD_MAX+2]; 3736 char *buf = hbuf + 1; 3737 hyphen_list *tem; 3738 for (tem = h; tem && len < WORD_MAX; tem = tem->next) { 3739 if (tem->hyphenation_code != 0) 3740 buf[len++] = tem->hyphenation_code; 3741 else 3742 break; 3743 } 3744 hyphen_list *nexth = tem; 3745 if (len > 2) { 3746 buf[len] = 0; 3747 unsigned char *pos 3748 = (unsigned char *)current_language->exceptions.lookup(buf); 3749 if (pos != 0) { 3750 int j = 0; 3751 int i = 1; 3752 for (tem = h; tem != 0; tem = tem->next, i++) 3753 if (pos[j] == i) { 3754 tem->hyphen = 1; 3755 j++; 3756 } 3757 } 3758 else { 3759 hbuf[0] = hbuf[len+1] = '.'; 3760 int num[WORD_MAX+3]; 3761 current_language->patterns.hyphenate(hbuf, len+2, num); 3762 int i; 3763 num[2] = 0; 3764 if (flags & 8) 3765 num[3] = 0; 3766 if (flags & 4) 3767 --len; 3768 for (i = 2, tem = h; i < len && tem; tem = tem->next, i++) 3769 if (num[i] & 1) 3770 tem->hyphen = 1; 3771 } 3772 } 3773 h = nexth; 3774 } 3775} 3776 3777static void do_hyphenation_patterns_file(int append) 3778{ 3779 symbol name = get_long_name(1); 3780 if (!name.is_null()) { 3781 if (!current_language) 3782 error("no current hyphenation language"); 3783 else 3784 current_language->patterns.read_patterns_file( 3785 name.contents(), append, 3786 ¤t_language->exceptions); 3787 } 3788 skip_line(); 3789} 3790 3791static void hyphenation_patterns_file() 3792{ 3793 do_hyphenation_patterns_file(0); 3794} 3795 3796static void hyphenation_patterns_file_append() 3797{ 3798 do_hyphenation_patterns_file(1); 3799} 3800 3801class hyphenation_language_reg : public reg { 3802public: 3803 const char *get_string(); 3804}; 3805 3806const char *hyphenation_language_reg::get_string() 3807{ 3808 return current_language ? current_language->name.contents() : ""; 3809} 3810 3811void init_hyphen_requests() 3812{ 3813 init_request("hw", hyphen_word); 3814 init_request("hla", set_hyphenation_language); 3815 init_request("hpf", hyphenation_patterns_file); 3816 init_request("hpfa", hyphenation_patterns_file_append); 3817 number_reg_dictionary.define(".hla", new hyphenation_language_reg); 3818} 3819