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