1114402Sru// -*- C++ -*- 2151497Sru/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005 3114402Sru Free Software Foundation, Inc. 4114402Sru Written by James Clark (jjc@jclark.com) 5114402Sru 6114402SruThis file is part of groff. 7114402Sru 8114402Srugroff is free software; you can redistribute it and/or modify it under 9114402Sruthe terms of the GNU General Public License as published by the Free 10114402SruSoftware Foundation; either version 2, or (at your option) any later 11114402Sruversion. 12114402Sru 13114402Srugroff is distributed in the hope that it will be useful, but WITHOUT ANY 14114402SruWARRANTY; without even the implied warranty of MERCHANTABILITY or 15114402SruFITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16114402Srufor more details. 17114402Sru 18114402SruYou should have received a copy of the GNU General Public License along 19114402Sruwith groff; see the file COPYING. If not, write to the Free Software 20151497SruFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */ 21114402Sru 22114402Sru#include "troff.h" 23114402Sru#include "dictionary.h" 24114402Sru#include "hvunits.h" 25151497Sru#include "stringclass.h" 26151497Sru#include "mtsm.h" 27114402Sru#include "env.h" 28114402Sru#include "request.h" 29114402Sru#include "node.h" 30114402Sru#include "token.h" 31114402Sru#include "div.h" 32114402Sru#include "reg.h" 33114402Sru#include "charinfo.h" 34114402Sru#include "macropath.h" 35114402Sru#include "input.h" 36114402Sru#include <math.h> 37114402Sru 38114402Srusymbol default_family("T"); 39114402Sru 40114402Sruenum { ADJUST_LEFT = 0, ADJUST_BOTH = 1, ADJUST_CENTER = 3, ADJUST_RIGHT = 5 }; 41114402Sru 42114402Sruenum { HYPHEN_LAST_LINE = 2, HYPHEN_LAST_CHARS = 4, HYPHEN_FIRST_CHARS = 8 }; 43114402Sru 44114402Srustruct env_list { 45114402Sru environment *env; 46114402Sru env_list *next; 47114402Sru env_list(environment *e, env_list *p) : env(e), next(p) {} 48114402Sru}; 49114402Sru 50114402Sruenv_list *env_stack; 51114402Sruconst int NENVIRONMENTS = 10; 52114402Sruenvironment *env_table[NENVIRONMENTS]; 53114402Srudictionary env_dictionary(10); 54114402Sruenvironment *curenv; 55114402Srustatic int next_line_number = 0; 56151497Sruextern int suppress_push; 57151497Sruextern statem *get_diversion_state(); 58114402Sru 59114402Srucharinfo *field_delimiter_char; 60114402Srucharinfo *padding_indicator_char; 61114402Sru 62114402Sruint translate_space_to_dummy = 0; 63114402Sru 64114402Sruclass pending_output_line { 65114402Sru node *nd; 66114402Sru int no_fill; 67151497Sru int was_centered; 68114402Sru vunits vs; 69114402Sru vunits post_vs; 70114402Sru hunits width; 71114402Sru#ifdef WIDOW_CONTROL 72114402Sru int last_line; // Is it the last line of the paragraph? 73114402Sru#endif /* WIDOW_CONTROL */ 74114402Srupublic: 75114402Sru pending_output_line *next; 76114402Sru 77151497Sru pending_output_line(node *, int, vunits, vunits, hunits, int, 78114402Sru pending_output_line * = 0); 79114402Sru ~pending_output_line(); 80114402Sru int output(); 81114402Sru 82114402Sru#ifdef WIDOW_CONTROL 83114402Sru friend void environment::mark_last_line(); 84151497Sru friend void environment::output(node *, int, vunits, vunits, hunits, int); 85114402Sru#endif /* WIDOW_CONTROL */ 86114402Sru}; 87114402Sru 88114402Srupending_output_line::pending_output_line(node *n, int nf, vunits v, vunits pv, 89151497Sru hunits w, int ce, 90151497Sru pending_output_line *p) 91151497Sru: nd(n), no_fill(nf), was_centered(ce), vs(v), post_vs(pv), width(w), 92114402Sru#ifdef WIDOW_CONTROL 93114402Sru last_line(0), 94114402Sru#endif /* WIDOW_CONTROL */ 95114402Sru next(p) 96114402Sru{ 97114402Sru} 98114402Sru 99114402Srupending_output_line::~pending_output_line() 100114402Sru{ 101114402Sru delete_node_list(nd); 102114402Sru} 103114402Sru 104114402Sruint pending_output_line::output() 105114402Sru{ 106114402Sru if (trap_sprung_flag) 107114402Sru return 0; 108114402Sru#ifdef WIDOW_CONTROL 109114402Sru if (next && next->last_line && !no_fill) { 110114402Sru curdiv->need(vs + post_vs + vunits(vresolution)); 111114402Sru if (trap_sprung_flag) { 112114402Sru next->last_line = 0; // Try to avoid infinite loops. 113114402Sru return 0; 114114402Sru } 115114402Sru } 116114402Sru#endif 117151497Sru curenv->construct_format_state(nd, was_centered, !no_fill); 118114402Sru curdiv->output(nd, no_fill, vs, post_vs, width); 119114402Sru nd = 0; 120114402Sru return 1; 121114402Sru} 122114402Sru 123151497Sruvoid environment::output(node *nd, int no_fill_flag, 124151497Sru vunits vs, vunits post_vs, 125151497Sru hunits width, int was_centered) 126114402Sru{ 127114402Sru#ifdef WIDOW_CONTROL 128114402Sru while (pending_lines) { 129114402Sru if (widow_control && !pending_lines->no_fill && !pending_lines->next) 130114402Sru break; 131114402Sru if (!pending_lines->output()) 132114402Sru break; 133114402Sru pending_output_line *tem = pending_lines; 134114402Sru pending_lines = pending_lines->next; 135114402Sru delete tem; 136114402Sru } 137114402Sru#else /* WIDOW_CONTROL */ 138114402Sru output_pending_lines(); 139114402Sru#endif /* WIDOW_CONTROL */ 140114402Sru if (!trap_sprung_flag && !pending_lines 141114402Sru#ifdef WIDOW_CONTROL 142151497Sru && (!widow_control || no_fill_flag) 143114402Sru#endif /* WIDOW_CONTROL */ 144114402Sru ) { 145151497Sru curenv->construct_format_state(nd, was_centered, !no_fill_flag); 146151497Sru curdiv->output(nd, no_fill_flag, vs, post_vs, width); 147114402Sru } else { 148114402Sru pending_output_line **p; 149114402Sru for (p = &pending_lines; *p; p = &(*p)->next) 150114402Sru ; 151151497Sru *p = new pending_output_line(nd, no_fill_flag, vs, post_vs, width, 152151497Sru was_centered); 153114402Sru } 154114402Sru} 155114402Sru 156114402Sru// a line from .tl goes at the head of the queue 157114402Sru 158151497Sruvoid environment::output_title(node *nd, int no_fill_flag, 159151497Sru vunits vs, vunits post_vs, 160151497Sru hunits width) 161114402Sru{ 162114402Sru if (!trap_sprung_flag) 163151497Sru curdiv->output(nd, no_fill_flag, vs, post_vs, width); 164114402Sru else 165151497Sru pending_lines = new pending_output_line(nd, no_fill_flag, vs, post_vs, 166151497Sru width, 0, pending_lines); 167114402Sru} 168114402Sru 169114402Sruvoid environment::output_pending_lines() 170114402Sru{ 171114402Sru while (pending_lines && pending_lines->output()) { 172114402Sru pending_output_line *tem = pending_lines; 173114402Sru pending_lines = pending_lines->next; 174114402Sru delete tem; 175114402Sru } 176114402Sru} 177114402Sru 178114402Sru#ifdef WIDOW_CONTROL 179114402Sru 180114402Sruvoid environment::mark_last_line() 181114402Sru{ 182114402Sru if (!widow_control || !pending_lines) 183114402Sru return; 184151497Sru pending_output_line *p; 185151497Sru for (p = pending_lines; p->next; p = p->next) 186114402Sru ; 187114402Sru if (!p->no_fill) 188114402Sru p->last_line = 1; 189114402Sru} 190114402Sru 191114402Sruvoid widow_control_request() 192114402Sru{ 193114402Sru int n; 194114402Sru if (has_arg() && get_integer(&n)) 195114402Sru curenv->widow_control = n != 0; 196114402Sru else 197114402Sru curenv->widow_control = 1; 198114402Sru skip_line(); 199114402Sru} 200114402Sru 201114402Sru#endif /* WIDOW_CONTROL */ 202114402Sru 203114402Sru/* font_size functions */ 204114402Sru 205114402Srusize_range *font_size::size_table = 0; 206114402Sruint font_size::nranges = 0; 207114402Sru 208114402Sruextern "C" { 209114402Sru 210114402Sruint compare_ranges(const void *p1, const void *p2) 211114402Sru{ 212114402Sru return ((size_range *)p1)->min - ((size_range *)p2)->min; 213114402Sru} 214114402Sru 215114402Sru} 216114402Sru 217114402Sruvoid font_size::init_size_table(int *sizes) 218114402Sru{ 219114402Sru nranges = 0; 220114402Sru while (sizes[nranges*2] != 0) 221114402Sru nranges++; 222114402Sru assert(nranges > 0); 223114402Sru size_table = new size_range[nranges]; 224114402Sru for (int i = 0; i < nranges; i++) { 225114402Sru size_table[i].min = sizes[i*2]; 226114402Sru size_table[i].max = sizes[i*2 + 1]; 227114402Sru } 228114402Sru qsort(size_table, nranges, sizeof(size_range), compare_ranges); 229114402Sru} 230114402Sru 231114402Srufont_size::font_size(int sp) 232114402Sru{ 233114402Sru for (int i = 0; i < nranges; i++) { 234114402Sru if (sp < size_table[i].min) { 235114402Sru if (i > 0 && size_table[i].min - sp >= sp - size_table[i - 1].max) 236114402Sru p = size_table[i - 1].max; 237114402Sru else 238114402Sru p = size_table[i].min; 239114402Sru return; 240114402Sru } 241114402Sru if (sp <= size_table[i].max) { 242114402Sru p = sp; 243114402Sru return; 244114402Sru } 245114402Sru } 246114402Sru p = size_table[nranges - 1].max; 247114402Sru} 248114402Sru 249114402Sruint font_size::to_units() 250114402Sru{ 251114402Sru return scale(p, units_per_inch, sizescale*72); 252114402Sru} 253114402Sru 254114402Sru// we can't do this in a static constructor because various dictionaries 255114402Sru// have to get initialized first 256114402Sru 257114402Sruvoid init_environments() 258114402Sru{ 259114402Sru curenv = env_table[0] = new environment("0"); 260114402Sru} 261114402Sru 262114402Sruvoid tab_character() 263114402Sru{ 264114402Sru curenv->tab_char = get_optional_char(); 265114402Sru skip_line(); 266114402Sru} 267114402Sru 268114402Sruvoid leader_character() 269114402Sru{ 270114402Sru curenv->leader_char = get_optional_char(); 271114402Sru skip_line(); 272114402Sru} 273114402Sru 274114402Sruvoid environment::add_char(charinfo *ci) 275114402Sru{ 276114402Sru int s; 277151497Sru node *gc_np = 0; 278114402Sru if (interrupted) 279114402Sru ; 280114402Sru // don't allow fields in dummy environments 281114402Sru else if (ci == field_delimiter_char && !dummy) { 282114402Sru if (current_field) 283114402Sru wrap_up_field(); 284114402Sru else 285114402Sru start_field(); 286114402Sru } 287114402Sru else if (current_field && ci == padding_indicator_char) 288114402Sru add_padding(); 289114402Sru else if (current_tab) { 290114402Sru if (tab_contents == 0) 291114402Sru tab_contents = new line_start_node; 292114402Sru if (ci != hyphen_indicator_char) 293151497Sru tab_contents = tab_contents->add_char(ci, this, &tab_width, &s, &gc_np); 294114402Sru else 295114402Sru tab_contents = tab_contents->add_discretionary_hyphen(); 296114402Sru } 297114402Sru else { 298114402Sru if (line == 0) 299114402Sru start_line(); 300151497Sru#if 0 301151497Sru fprintf(stderr, "current line is\n"); 302151497Sru line->debug_node_list(); 303151497Sru#endif 304114402Sru if (ci != hyphen_indicator_char) 305151497Sru line = line->add_char(ci, this, &width_total, &space_total, &gc_np); 306114402Sru else 307114402Sru line = line->add_discretionary_hyphen(); 308114402Sru } 309151497Sru#if 0 310151497Sru fprintf(stderr, "now after we have added character the line is\n"); 311151497Sru line->debug_node_list(); 312151497Sru#endif 313151497Sru if ((!suppress_push) && gc_np) { 314151497Sru if (gc_np && (gc_np->state == 0)) { 315151497Sru gc_np->state = construct_state(0); 316151497Sru gc_np->push_state = get_diversion_state(); 317151497Sru } 318151497Sru else if (line && (line->state == 0)) { 319151497Sru line->state = construct_state(0); 320151497Sru line->push_state = get_diversion_state(); 321151497Sru } 322151497Sru } 323151497Sru#if 0 324151497Sru fprintf(stderr, "now we have possibly added the state the line is\n"); 325151497Sru line->debug_node_list(); 326151497Sru#endif 327114402Sru} 328114402Sru 329114402Srunode *environment::make_char_node(charinfo *ci) 330114402Sru{ 331114402Sru return make_node(ci, this); 332114402Sru} 333114402Sru 334114402Sruvoid environment::add_node(node *n) 335114402Sru{ 336114402Sru if (n == 0) 337114402Sru return; 338151497Sru if (!suppress_push) { 339151497Sru if (n->is_special && n->state == NULL) 340151497Sru n->state = construct_state(0); 341151497Sru n->push_state = get_diversion_state(); 342151497Sru } 343151497Sru 344114402Sru if (current_tab || current_field) 345114402Sru n->freeze_space(); 346114402Sru if (interrupted) { 347114402Sru delete n; 348114402Sru } 349114402Sru else if (current_tab) { 350114402Sru n->next = tab_contents; 351114402Sru tab_contents = n; 352114402Sru tab_width += n->width(); 353114402Sru } 354114402Sru else { 355114402Sru if (line == 0) { 356114402Sru if (discarding && n->discardable()) { 357114402Sru // XXX possibly: input_line_start -= n->width(); 358114402Sru delete n; 359114402Sru return; 360114402Sru } 361114402Sru start_line(); 362114402Sru } 363114402Sru width_total += n->width(); 364114402Sru space_total += n->nspaces(); 365114402Sru n->next = line; 366114402Sru line = n; 367151497Sru construct_new_line_state(line); 368114402Sru } 369114402Sru} 370114402Sru 371114402Sruvoid environment::add_hyphen_indicator() 372114402Sru{ 373114402Sru if (current_tab || interrupted || current_field 374114402Sru || hyphen_indicator_char != 0) 375114402Sru return; 376114402Sru if (line == 0) 377114402Sru start_line(); 378114402Sru line = line->add_discretionary_hyphen(); 379114402Sru} 380114402Sru 381114402Sruint environment::get_hyphenation_flags() 382114402Sru{ 383114402Sru return hyphenation_flags; 384114402Sru} 385114402Sru 386114402Sruint environment::get_hyphen_line_max() 387114402Sru{ 388114402Sru return hyphen_line_max; 389114402Sru} 390114402Sru 391114402Sruint environment::get_hyphen_line_count() 392114402Sru{ 393114402Sru return hyphen_line_count; 394114402Sru} 395114402Sru 396114402Sruint environment::get_center_lines() 397114402Sru{ 398114402Sru return center_lines; 399114402Sru} 400114402Sru 401114402Sruint environment::get_right_justify_lines() 402114402Sru{ 403114402Sru return right_justify_lines; 404114402Sru} 405114402Sru 406114402Sruvoid environment::add_italic_correction() 407114402Sru{ 408114402Sru if (current_tab) { 409114402Sru if (tab_contents) 410114402Sru tab_contents = tab_contents->add_italic_correction(&tab_width); 411114402Sru } 412114402Sru else if (line) 413114402Sru line = line->add_italic_correction(&width_total); 414114402Sru} 415114402Sru 416114402Sruvoid environment::space_newline() 417114402Sru{ 418114402Sru assert(!current_tab && !current_field); 419114402Sru if (interrupted) 420114402Sru return; 421114402Sru hunits x = H0; 422114402Sru hunits sw = env_space_width(this); 423114402Sru hunits ssw = env_sentence_space_width(this); 424114402Sru if (!translate_space_to_dummy) { 425114402Sru x = sw; 426114402Sru if (node_list_ends_sentence(line) == 1) 427114402Sru x += ssw; 428114402Sru } 429114402Sru width_list *w = new width_list(sw, ssw); 430114402Sru if (node_list_ends_sentence(line) == 1) 431114402Sru w->next = new width_list(sw, ssw); 432114402Sru if (line != 0 && line->merge_space(x, sw, ssw)) { 433114402Sru width_total += x; 434114402Sru return; 435114402Sru } 436114402Sru add_node(new word_space_node(x, get_fill_color(), w)); 437114402Sru possibly_break_line(0, spread_flag); 438114402Sru spread_flag = 0; 439114402Sru} 440114402Sru 441114402Sruvoid environment::space() 442114402Sru{ 443114402Sru space(env_space_width(this), env_sentence_space_width(this)); 444114402Sru} 445114402Sru 446114402Sruvoid environment::space(hunits space_width, hunits sentence_space_width) 447114402Sru{ 448114402Sru if (interrupted) 449114402Sru return; 450114402Sru if (current_field && padding_indicator_char == 0) { 451114402Sru add_padding(); 452114402Sru return; 453114402Sru } 454114402Sru hunits x = translate_space_to_dummy ? H0 : space_width; 455114402Sru node *p = current_tab ? tab_contents : line; 456114402Sru hunits *tp = current_tab ? &tab_width : &width_total; 457114402Sru if (p && p->nspaces() == 1 && p->width() == x 458114402Sru && node_list_ends_sentence(p->next) == 1) { 459114402Sru hunits xx = translate_space_to_dummy ? H0 : sentence_space_width; 460114402Sru if (p->merge_space(xx, space_width, sentence_space_width)) { 461114402Sru *tp += xx; 462114402Sru return; 463114402Sru } 464114402Sru } 465114402Sru if (p && p->merge_space(x, space_width, sentence_space_width)) { 466114402Sru *tp += x; 467114402Sru return; 468114402Sru } 469114402Sru add_node(new word_space_node(x, 470114402Sru get_fill_color(), 471114402Sru new width_list(space_width, 472114402Sru sentence_space_width))); 473114402Sru possibly_break_line(0, spread_flag); 474114402Sru spread_flag = 0; 475114402Sru} 476114402Sru 477114402Srunode *do_underline_special(int); 478114402Sru 479114402Sruvoid environment::set_font(symbol nm) 480114402Sru{ 481114402Sru if (interrupted) 482114402Sru return; 483114402Sru if (nm == symbol("P") || nm.is_empty()) { 484114402Sru if (family->make_definite(prev_fontno) < 0) 485114402Sru return; 486114402Sru int tem = fontno; 487114402Sru fontno = prev_fontno; 488114402Sru prev_fontno = tem; 489114402Sru } 490114402Sru else { 491114402Sru prev_fontno = fontno; 492114402Sru int n = symbol_fontno(nm); 493114402Sru if (n < 0) { 494114402Sru n = next_available_font_position(); 495114402Sru if (!mount_font(n, nm)) 496114402Sru return; 497114402Sru } 498114402Sru if (family->make_definite(n) < 0) 499114402Sru return; 500114402Sru fontno = n; 501114402Sru } 502114402Sru if (underline_spaces && fontno != prev_fontno) { 503114402Sru if (fontno == get_underline_fontno()) 504114402Sru add_node(do_underline_special(1)); 505114402Sru if (prev_fontno == get_underline_fontno()) 506114402Sru add_node(do_underline_special(0)); 507114402Sru } 508114402Sru} 509114402Sru 510114402Sruvoid environment::set_font(int n) 511114402Sru{ 512114402Sru if (interrupted) 513114402Sru return; 514114402Sru if (is_good_fontno(n)) { 515114402Sru prev_fontno = fontno; 516114402Sru fontno = n; 517114402Sru } 518114402Sru else 519114402Sru warning(WARN_FONT, "bad font number"); 520114402Sru} 521114402Sru 522114402Sruvoid environment::set_family(symbol fam) 523114402Sru{ 524114402Sru if (interrupted) 525114402Sru return; 526114402Sru if (fam.is_null() || fam.is_empty()) { 527114402Sru if (prev_family->make_definite(fontno) < 0) 528114402Sru return; 529114402Sru font_family *tem = family; 530114402Sru family = prev_family; 531114402Sru prev_family = tem; 532114402Sru } 533114402Sru else { 534114402Sru font_family *f = lookup_family(fam); 535114402Sru if (f->make_definite(fontno) < 0) 536114402Sru return; 537114402Sru prev_family = family; 538114402Sru family = f; 539114402Sru } 540114402Sru} 541114402Sru 542114402Sruvoid environment::set_size(int n) 543114402Sru{ 544114402Sru if (interrupted) 545114402Sru return; 546114402Sru if (n == 0) { 547114402Sru font_size temp = prev_size; 548114402Sru prev_size = size; 549114402Sru size = temp; 550114402Sru int temp2 = prev_requested_size; 551114402Sru prev_requested_size = requested_size; 552114402Sru requested_size = temp2; 553114402Sru } 554114402Sru else { 555114402Sru prev_size = size; 556114402Sru size = font_size(n); 557114402Sru prev_requested_size = requested_size; 558114402Sru requested_size = n; 559114402Sru } 560114402Sru} 561114402Sru 562114402Sruvoid environment::set_char_height(int n) 563114402Sru{ 564114402Sru if (interrupted) 565114402Sru return; 566114402Sru if (n == requested_size || n <= 0) 567114402Sru char_height = 0; 568114402Sru else 569114402Sru char_height = n; 570114402Sru} 571114402Sru 572114402Sruvoid environment::set_char_slant(int n) 573114402Sru{ 574114402Sru if (interrupted) 575114402Sru return; 576114402Sru char_slant = n; 577114402Sru} 578114402Sru 579114402Srucolor *environment::get_prev_glyph_color() 580114402Sru{ 581114402Sru return prev_glyph_color; 582114402Sru} 583114402Sru 584114402Srucolor *environment::get_glyph_color() 585114402Sru{ 586114402Sru return glyph_color; 587114402Sru} 588114402Sru 589114402Srucolor *environment::get_prev_fill_color() 590114402Sru{ 591114402Sru return prev_fill_color; 592114402Sru} 593114402Sru 594114402Srucolor *environment::get_fill_color() 595114402Sru{ 596114402Sru return fill_color; 597114402Sru} 598114402Sru 599114402Sruvoid environment::set_glyph_color(color *c) 600114402Sru{ 601114402Sru if (interrupted) 602114402Sru return; 603114402Sru curenv->prev_glyph_color = curenv->glyph_color; 604114402Sru curenv->glyph_color = c; 605114402Sru} 606114402Sru 607114402Sruvoid environment::set_fill_color(color *c) 608114402Sru{ 609114402Sru if (interrupted) 610114402Sru return; 611114402Sru curenv->prev_fill_color = curenv->fill_color; 612114402Sru curenv->fill_color = c; 613114402Sru} 614114402Sru 615114402Sruenvironment::environment(symbol nm) 616114402Sru: dummy(0), 617114402Sru prev_line_length((units_per_inch*13)/2), 618114402Sru line_length((units_per_inch*13)/2), 619114402Sru prev_title_length((units_per_inch*13)/2), 620114402Sru title_length((units_per_inch*13)/2), 621114402Sru prev_size(sizescale*10), 622114402Sru size(sizescale*10), 623114402Sru requested_size(sizescale*10), 624114402Sru prev_requested_size(sizescale*10), 625114402Sru char_height(0), 626114402Sru char_slant(0), 627114402Sru space_size(12), 628114402Sru sentence_space_size(12), 629114402Sru adjust_mode(ADJUST_BOTH), 630114402Sru fill(1), 631114402Sru interrupted(0), 632114402Sru prev_line_interrupted(0), 633114402Sru center_lines(0), 634114402Sru right_justify_lines(0), 635114402Sru prev_vertical_spacing(points_to_units(12)), 636114402Sru vertical_spacing(points_to_units(12)), 637114402Sru prev_post_vertical_spacing(0), 638114402Sru post_vertical_spacing(0), 639114402Sru prev_line_spacing(1), 640114402Sru line_spacing(1), 641114402Sru prev_indent(0), 642114402Sru indent(0), 643114402Sru temporary_indent(0), 644114402Sru have_temporary_indent(0), 645114402Sru underline_lines(0), 646114402Sru underline_spaces(0), 647114402Sru input_trap_count(0), 648114402Sru continued_input_trap(0), 649114402Sru line(0), 650114402Sru prev_text_length(0), 651114402Sru width_total(0), 652114402Sru space_total(0), 653114402Sru input_line_start(0), 654114402Sru line_tabs(0), 655114402Sru current_tab(TAB_NONE), 656114402Sru leader_node(0), 657114402Sru tab_char(0), 658114402Sru leader_char(charset_table['.']), 659114402Sru current_field(0), 660114402Sru discarding(0), 661114402Sru spread_flag(0), 662114402Sru margin_character_flags(0), 663114402Sru margin_character_node(0), 664114402Sru margin_character_distance(points_to_units(10)), 665114402Sru numbering_nodes(0), 666114402Sru number_text_separation(1), 667114402Sru line_number_indent(0), 668114402Sru line_number_multiple(1), 669114402Sru no_number_count(0), 670114402Sru hyphenation_flags(1), 671114402Sru hyphen_line_count(0), 672114402Sru hyphen_line_max(-1), 673114402Sru hyphenation_space(H0), 674114402Sru hyphenation_margin(H0), 675114402Sru composite(0), 676114402Sru pending_lines(0), 677114402Sru#ifdef WIDOW_CONTROL 678114402Sru widow_control(0), 679114402Sru#endif /* WIDOW_CONTROL */ 680114402Sru glyph_color(&default_color), 681114402Sru prev_glyph_color(&default_color), 682114402Sru fill_color(&default_color), 683114402Sru prev_fill_color(&default_color), 684151497Sru seen_space(0), 685151497Sru seen_eol(0), 686151497Sru suppress_next_eol(0), 687151497Sru seen_break(0), 688151497Sru tabs(units_per_inch/2, TAB_LEFT), 689114402Sru name(nm), 690114402Sru control_char('.'), 691114402Sru no_break_control_char('\''), 692114402Sru hyphen_indicator_char(0) 693114402Sru{ 694114402Sru prev_family = family = lookup_family(default_family); 695114402Sru prev_fontno = fontno = 1; 696114402Sru if (!is_good_fontno(1)) 697114402Sru fatal("font number 1 not a valid font"); 698114402Sru if (family->make_definite(1) < 0) 699114402Sru fatal("invalid default family `%1'", default_family.contents()); 700114402Sru prev_fontno = fontno; 701114402Sru} 702114402Sru 703114402Sruenvironment::environment(const environment *e) 704114402Sru: dummy(1), 705114402Sru prev_line_length(e->prev_line_length), 706114402Sru line_length(e->line_length), 707114402Sru prev_title_length(e->prev_title_length), 708114402Sru title_length(e->title_length), 709114402Sru prev_size(e->prev_size), 710114402Sru size(e->size), 711114402Sru requested_size(e->requested_size), 712114402Sru prev_requested_size(e->prev_requested_size), 713114402Sru char_height(e->char_height), 714114402Sru char_slant(e->char_slant), 715114402Sru prev_fontno(e->prev_fontno), 716114402Sru fontno(e->fontno), 717114402Sru prev_family(e->prev_family), 718114402Sru family(e->family), 719114402Sru space_size(e->space_size), 720114402Sru sentence_space_size(e->sentence_space_size), 721114402Sru adjust_mode(e->adjust_mode), 722114402Sru fill(e->fill), 723114402Sru interrupted(0), 724114402Sru prev_line_interrupted(0), 725114402Sru center_lines(0), 726114402Sru right_justify_lines(0), 727114402Sru prev_vertical_spacing(e->prev_vertical_spacing), 728114402Sru vertical_spacing(e->vertical_spacing), 729114402Sru prev_post_vertical_spacing(e->prev_post_vertical_spacing), 730114402Sru post_vertical_spacing(e->post_vertical_spacing), 731114402Sru prev_line_spacing(e->prev_line_spacing), 732114402Sru line_spacing(e->line_spacing), 733114402Sru prev_indent(e->prev_indent), 734114402Sru indent(e->indent), 735114402Sru temporary_indent(0), 736114402Sru have_temporary_indent(0), 737114402Sru underline_lines(0), 738114402Sru underline_spaces(0), 739114402Sru input_trap_count(0), 740114402Sru continued_input_trap(0), 741114402Sru line(0), 742114402Sru prev_text_length(e->prev_text_length), 743114402Sru width_total(0), 744114402Sru space_total(0), 745114402Sru input_line_start(0), 746114402Sru line_tabs(e->line_tabs), 747114402Sru current_tab(TAB_NONE), 748114402Sru leader_node(0), 749114402Sru tab_char(e->tab_char), 750114402Sru leader_char(e->leader_char), 751114402Sru current_field(0), 752114402Sru discarding(0), 753114402Sru spread_flag(0), 754114402Sru margin_character_flags(e->margin_character_flags), 755114402Sru margin_character_node(e->margin_character_node), 756114402Sru margin_character_distance(e->margin_character_distance), 757114402Sru numbering_nodes(0), 758114402Sru number_text_separation(e->number_text_separation), 759114402Sru line_number_indent(e->line_number_indent), 760114402Sru line_number_multiple(e->line_number_multiple), 761114402Sru no_number_count(e->no_number_count), 762114402Sru hyphenation_flags(e->hyphenation_flags), 763114402Sru hyphen_line_count(0), 764114402Sru hyphen_line_max(e->hyphen_line_max), 765114402Sru hyphenation_space(e->hyphenation_space), 766114402Sru hyphenation_margin(e->hyphenation_margin), 767114402Sru composite(0), 768114402Sru pending_lines(0), 769114402Sru#ifdef WIDOW_CONTROL 770114402Sru widow_control(e->widow_control), 771114402Sru#endif /* WIDOW_CONTROL */ 772114402Sru glyph_color(e->glyph_color), 773114402Sru prev_glyph_color(e->prev_glyph_color), 774114402Sru fill_color(e->fill_color), 775114402Sru prev_fill_color(e->prev_fill_color), 776151497Sru seen_space(e->seen_space), 777151497Sru seen_eol(e->seen_eol), 778151497Sru suppress_next_eol(e->suppress_next_eol), 779151497Sru seen_break(e->seen_break), 780151497Sru tabs(e->tabs), 781114402Sru name(e->name), // so that eg `.if "\n[.ev]"0"' works 782114402Sru control_char(e->control_char), 783114402Sru no_break_control_char(e->no_break_control_char), 784114402Sru hyphen_indicator_char(e->hyphen_indicator_char) 785114402Sru{ 786114402Sru} 787114402Sru 788114402Sruvoid environment::copy(const environment *e) 789114402Sru{ 790114402Sru prev_line_length = e->prev_line_length; 791114402Sru line_length = e->line_length; 792114402Sru prev_title_length = e->prev_title_length; 793114402Sru title_length = e->title_length; 794114402Sru prev_size = e->prev_size; 795114402Sru size = e->size; 796114402Sru prev_requested_size = e->prev_requested_size; 797114402Sru requested_size = e->requested_size; 798114402Sru char_height = e->char_height; 799114402Sru char_slant = e->char_slant; 800114402Sru space_size = e->space_size; 801114402Sru sentence_space_size = e->sentence_space_size; 802114402Sru adjust_mode = e->adjust_mode; 803114402Sru fill = e->fill; 804114402Sru interrupted = 0; 805114402Sru prev_line_interrupted = 0; 806114402Sru center_lines = 0; 807114402Sru right_justify_lines = 0; 808114402Sru prev_vertical_spacing = e->prev_vertical_spacing; 809114402Sru vertical_spacing = e->vertical_spacing; 810114402Sru prev_post_vertical_spacing = e->prev_post_vertical_spacing, 811114402Sru post_vertical_spacing = e->post_vertical_spacing, 812114402Sru prev_line_spacing = e->prev_line_spacing; 813114402Sru line_spacing = e->line_spacing; 814114402Sru prev_indent = e->prev_indent; 815114402Sru indent = e->indent; 816114402Sru have_temporary_indent = 0; 817114402Sru temporary_indent = 0; 818114402Sru underline_lines = 0; 819114402Sru underline_spaces = 0; 820114402Sru input_trap_count = 0; 821114402Sru continued_input_trap = 0; 822114402Sru prev_text_length = e->prev_text_length; 823114402Sru width_total = 0; 824114402Sru space_total = 0; 825114402Sru input_line_start = 0; 826114402Sru control_char = e->control_char; 827114402Sru no_break_control_char = e->no_break_control_char; 828114402Sru hyphen_indicator_char = e->hyphen_indicator_char; 829114402Sru spread_flag = 0; 830114402Sru line = 0; 831114402Sru pending_lines = 0; 832114402Sru discarding = 0; 833114402Sru tabs = e->tabs; 834114402Sru line_tabs = e->line_tabs; 835114402Sru current_tab = TAB_NONE; 836114402Sru current_field = 0; 837114402Sru margin_character_flags = e->margin_character_flags; 838114402Sru margin_character_node = e->margin_character_node; 839114402Sru margin_character_distance = e->margin_character_distance; 840114402Sru numbering_nodes = 0; 841114402Sru number_text_separation = e->number_text_separation; 842114402Sru line_number_multiple = e->line_number_multiple; 843114402Sru line_number_indent = e->line_number_indent; 844114402Sru no_number_count = e->no_number_count; 845114402Sru tab_char = e->tab_char; 846114402Sru leader_char = e->leader_char; 847114402Sru hyphenation_flags = e->hyphenation_flags; 848114402Sru fontno = e->fontno; 849114402Sru prev_fontno = e->prev_fontno; 850114402Sru dummy = e->dummy; 851114402Sru family = e->family; 852114402Sru prev_family = e->prev_family; 853114402Sru leader_node = 0; 854114402Sru#ifdef WIDOW_CONTROL 855114402Sru widow_control = e->widow_control; 856114402Sru#endif /* WIDOW_CONTROL */ 857114402Sru hyphen_line_max = e->hyphen_line_max; 858114402Sru hyphen_line_count = 0; 859114402Sru hyphenation_space = e->hyphenation_space; 860114402Sru hyphenation_margin = e->hyphenation_margin; 861114402Sru composite = 0; 862114402Sru glyph_color= e->glyph_color; 863114402Sru prev_glyph_color = e->prev_glyph_color; 864114402Sru fill_color = e->fill_color; 865114402Sru prev_fill_color = e->prev_fill_color; 866114402Sru} 867114402Sru 868114402Sruenvironment::~environment() 869114402Sru{ 870114402Sru delete leader_node; 871114402Sru delete_node_list(line); 872114402Sru delete_node_list(numbering_nodes); 873114402Sru} 874114402Sru 875114402Sruhunits environment::get_input_line_position() 876114402Sru{ 877114402Sru hunits n; 878114402Sru if (line == 0) 879114402Sru n = -input_line_start; 880114402Sru else 881114402Sru n = width_total - input_line_start; 882114402Sru if (current_tab) 883114402Sru n += tab_width; 884114402Sru return n; 885114402Sru} 886114402Sru 887114402Sruvoid environment::set_input_line_position(hunits n) 888114402Sru{ 889114402Sru input_line_start = line == 0 ? -n : width_total - n; 890114402Sru if (current_tab) 891114402Sru input_line_start += tab_width; 892114402Sru} 893114402Sru 894114402Sruhunits environment::get_line_length() 895114402Sru{ 896114402Sru return line_length; 897114402Sru} 898114402Sru 899114402Sruhunits environment::get_saved_line_length() 900114402Sru{ 901114402Sru if (line) 902114402Sru return target_text_length + saved_indent; 903114402Sru else 904114402Sru return line_length; 905114402Sru} 906114402Sru 907114402Sruvunits environment::get_vertical_spacing() 908114402Sru{ 909114402Sru return vertical_spacing; 910114402Sru} 911114402Sru 912114402Sruvunits environment::get_post_vertical_spacing() 913114402Sru{ 914114402Sru return post_vertical_spacing; 915114402Sru} 916114402Sru 917114402Sruint environment::get_line_spacing() 918114402Sru{ 919114402Sru return line_spacing; 920114402Sru} 921114402Sru 922114402Sruvunits environment::total_post_vertical_spacing() 923114402Sru{ 924114402Sru vunits tem(post_vertical_spacing); 925114402Sru if (line_spacing > 1) 926114402Sru tem += (line_spacing - 1)*vertical_spacing; 927114402Sru return tem; 928114402Sru} 929114402Sru 930114402Sruint environment::get_bold() 931114402Sru{ 932114402Sru return get_bold_fontno(fontno); 933114402Sru} 934114402Sru 935114402Sruhunits environment::get_digit_width() 936114402Sru{ 937114402Sru return env_digit_width(this); 938114402Sru} 939114402Sru 940114402Sruint environment::get_adjust_mode() 941114402Sru{ 942114402Sru return adjust_mode; 943114402Sru} 944114402Sru 945114402Sruint environment::get_fill() 946114402Sru{ 947114402Sru return fill; 948114402Sru} 949114402Sru 950114402Sruhunits environment::get_indent() 951114402Sru{ 952114402Sru return indent; 953114402Sru} 954114402Sru 955114402Sruhunits environment::get_saved_indent() 956114402Sru{ 957114402Sru if (line) 958114402Sru return saved_indent; 959114402Sru else if (have_temporary_indent) 960114402Sru return temporary_indent; 961114402Sru else 962114402Sru return indent; 963114402Sru} 964114402Sru 965114402Sruhunits environment::get_temporary_indent() 966114402Sru{ 967114402Sru return temporary_indent; 968114402Sru} 969114402Sru 970114402Sruhunits environment::get_title_length() 971114402Sru{ 972114402Sru return title_length; 973114402Sru} 974114402Sru 975114402Srunode *environment::get_prev_char() 976114402Sru{ 977114402Sru for (node *n = current_tab ? tab_contents : line; n; n = n->next) { 978114402Sru node *last = n->last_char_node(); 979114402Sru if (last) 980114402Sru return last; 981114402Sru } 982114402Sru return 0; 983114402Sru} 984114402Sru 985114402Sruhunits environment::get_prev_char_width() 986114402Sru{ 987114402Sru node *last = get_prev_char(); 988114402Sru if (!last) 989114402Sru return H0; 990114402Sru return last->width(); 991114402Sru} 992114402Sru 993114402Sruhunits environment::get_prev_char_skew() 994114402Sru{ 995114402Sru node *last = get_prev_char(); 996114402Sru if (!last) 997114402Sru return H0; 998114402Sru return last->skew(); 999114402Sru} 1000114402Sru 1001114402Sruvunits environment::get_prev_char_height() 1002114402Sru{ 1003114402Sru node *last = get_prev_char(); 1004114402Sru if (!last) 1005114402Sru return V0; 1006114402Sru vunits min, max; 1007114402Sru last->vertical_extent(&min, &max); 1008114402Sru return -min; 1009114402Sru} 1010114402Sru 1011114402Sruvunits environment::get_prev_char_depth() 1012114402Sru{ 1013114402Sru node *last = get_prev_char(); 1014114402Sru if (!last) 1015114402Sru return V0; 1016114402Sru vunits min, max; 1017114402Sru last->vertical_extent(&min, &max); 1018114402Sru return max; 1019114402Sru} 1020114402Sru 1021114402Sruhunits environment::get_text_length() 1022114402Sru{ 1023114402Sru hunits n = line == 0 ? H0 : width_total; 1024114402Sru if (current_tab) 1025114402Sru n += tab_width; 1026114402Sru return n; 1027114402Sru} 1028114402Sru 1029114402Sruhunits environment::get_prev_text_length() 1030114402Sru{ 1031114402Sru return prev_text_length; 1032114402Sru} 1033114402Sru 1034114402Sru 1035114402Srustatic int sb_reg_contents = 0; 1036114402Srustatic int st_reg_contents = 0; 1037114402Srustatic int ct_reg_contents = 0; 1038114402Srustatic int rsb_reg_contents = 0; 1039114402Srustatic int rst_reg_contents = 0; 1040114402Srustatic int skw_reg_contents = 0; 1041114402Srustatic int ssc_reg_contents = 0; 1042114402Sru 1043114402Sruvoid environment::width_registers() 1044114402Sru{ 1045114402Sru // this is used to implement \w; it sets the st, sb, ct registers 1046114402Sru vunits min = 0, max = 0, cur = 0; 1047114402Sru int character_type = 0; 1048114402Sru ssc_reg_contents = line ? line->subscript_correction().to_units() : 0; 1049114402Sru skw_reg_contents = line ? line->skew().to_units() : 0; 1050114402Sru line = reverse_node_list(line); 1051114402Sru vunits real_min = V0; 1052114402Sru vunits real_max = V0; 1053114402Sru vunits v1, v2; 1054114402Sru for (node *tem = line; tem; tem = tem->next) { 1055114402Sru tem->vertical_extent(&v1, &v2); 1056114402Sru v1 += cur; 1057114402Sru if (v1 < real_min) 1058114402Sru real_min = v1; 1059114402Sru v2 += cur; 1060114402Sru if (v2 > real_max) 1061114402Sru real_max = v2; 1062114402Sru if ((cur += tem->vertical_width()) < min) 1063114402Sru min = cur; 1064114402Sru else if (cur > max) 1065114402Sru max = cur; 1066114402Sru character_type |= tem->character_type(); 1067114402Sru } 1068114402Sru line = reverse_node_list(line); 1069114402Sru st_reg_contents = -min.to_units(); 1070114402Sru sb_reg_contents = -max.to_units(); 1071114402Sru rst_reg_contents = -real_min.to_units(); 1072114402Sru rsb_reg_contents = -real_max.to_units(); 1073114402Sru ct_reg_contents = character_type; 1074114402Sru} 1075114402Sru 1076114402Srunode *environment::extract_output_line() 1077114402Sru{ 1078114402Sru if (current_tab) 1079114402Sru wrap_up_tab(); 1080114402Sru node *n = line; 1081114402Sru line = 0; 1082114402Sru return n; 1083114402Sru} 1084114402Sru 1085114402Sru/* environment related requests */ 1086114402Sru 1087114402Sruvoid environment_switch() 1088114402Sru{ 1089114402Sru int pop = 0; // 1 means pop, 2 means pop but no error message on underflow 1090114402Sru if (curenv->is_dummy()) 1091114402Sru error("can't switch environments when current environment is dummy"); 1092114402Sru else if (!has_arg()) 1093114402Sru pop = 1; 1094114402Sru else { 1095114402Sru symbol nm; 1096114402Sru if (!tok.delimiter()) { 1097114402Sru // It looks like a number. 1098114402Sru int n; 1099114402Sru if (get_integer(&n)) { 1100114402Sru if (n >= 0 && n < NENVIRONMENTS) { 1101114402Sru env_stack = new env_list(curenv, env_stack); 1102114402Sru if (env_table[n] == 0) 1103114402Sru env_table[n] = new environment(i_to_a(n)); 1104114402Sru curenv = env_table[n]; 1105114402Sru } 1106114402Sru else 1107114402Sru nm = i_to_a(n); 1108114402Sru } 1109114402Sru else 1110114402Sru pop = 2; 1111114402Sru } 1112114402Sru else { 1113114402Sru nm = get_long_name(1); 1114114402Sru if (nm.is_null()) 1115114402Sru pop = 2; 1116114402Sru } 1117114402Sru if (!nm.is_null()) { 1118114402Sru environment *e = (environment *)env_dictionary.lookup(nm); 1119114402Sru if (!e) { 1120114402Sru e = new environment(nm); 1121114402Sru (void)env_dictionary.lookup(nm, e); 1122114402Sru } 1123114402Sru env_stack = new env_list(curenv, env_stack); 1124114402Sru curenv = e; 1125114402Sru } 1126114402Sru } 1127114402Sru if (pop) { 1128114402Sru if (env_stack == 0) { 1129114402Sru if (pop == 1) 1130114402Sru error("environment stack underflow"); 1131114402Sru } 1132114402Sru else { 1133151497Sru int seen_space = curenv->seen_space; 1134151497Sru int seen_eol = curenv->seen_eol; 1135151497Sru int suppress_next_eol = curenv->suppress_next_eol; 1136114402Sru curenv = env_stack->env; 1137151497Sru curenv->seen_space = seen_space; 1138151497Sru curenv->seen_eol = seen_eol; 1139151497Sru curenv->suppress_next_eol = suppress_next_eol; 1140114402Sru env_list *tem = env_stack; 1141114402Sru env_stack = env_stack->next; 1142114402Sru delete tem; 1143114402Sru } 1144114402Sru } 1145114402Sru skip_line(); 1146114402Sru} 1147114402Sru 1148114402Sruvoid environment_copy() 1149114402Sru{ 1150114402Sru symbol nm; 1151114402Sru environment *e=0; 1152114402Sru tok.skip(); 1153114402Sru if (!tok.delimiter()) { 1154114402Sru // It looks like a number. 1155114402Sru int n; 1156114402Sru if (get_integer(&n)) { 1157114402Sru if (n >= 0 && n < NENVIRONMENTS) 1158114402Sru e = env_table[n]; 1159114402Sru else 1160114402Sru nm = i_to_a(n); 1161114402Sru } 1162114402Sru } 1163114402Sru else 1164114402Sru nm = get_long_name(1); 1165114402Sru if (!e && !nm.is_null()) 1166114402Sru e = (environment *)env_dictionary.lookup(nm); 1167114402Sru if (e == 0) { 1168114402Sru error("No environment to copy from"); 1169114402Sru return; 1170114402Sru } 1171114402Sru else 1172114402Sru curenv->copy(e); 1173114402Sru skip_line(); 1174114402Sru} 1175114402Sru 1176151497Sruvoid fill_color_change() 1177151497Sru{ 1178151497Sru symbol s = get_name(); 1179151497Sru if (s.is_null()) 1180151497Sru curenv->set_fill_color(curenv->get_prev_fill_color()); 1181151497Sru else 1182151497Sru do_fill_color(s); 1183151497Sru skip_line(); 1184151497Sru} 1185151497Sru 1186151497Sruvoid glyph_color_change() 1187151497Sru{ 1188151497Sru symbol s = get_name(); 1189151497Sru if (s.is_null()) 1190151497Sru curenv->set_glyph_color(curenv->get_prev_glyph_color()); 1191151497Sru else 1192151497Sru do_glyph_color(s); 1193151497Sru skip_line(); 1194151497Sru} 1195151497Sru 1196114402Srustatic symbol P_symbol("P"); 1197114402Sru 1198114402Sruvoid font_change() 1199114402Sru{ 1200114402Sru symbol s = get_name(); 1201114402Sru int is_number = 1; 1202114402Sru if (s.is_null() || s == P_symbol) { 1203114402Sru s = P_symbol; 1204114402Sru is_number = 0; 1205114402Sru } 1206114402Sru else { 1207114402Sru for (const char *p = s.contents(); p != 0 && *p != 0; p++) 1208114402Sru if (!csdigit(*p)) { 1209114402Sru is_number = 0; 1210114402Sru break; 1211114402Sru } 1212114402Sru } 1213114402Sru if (is_number) 1214114402Sru curenv->set_font(atoi(s.contents())); 1215114402Sru else 1216114402Sru curenv->set_font(s); 1217114402Sru skip_line(); 1218114402Sru} 1219114402Sru 1220114402Sruvoid family_change() 1221114402Sru{ 1222114402Sru symbol s = get_name(); 1223114402Sru curenv->set_family(s); 1224114402Sru skip_line(); 1225114402Sru} 1226114402Sru 1227114402Sruvoid point_size() 1228114402Sru{ 1229114402Sru int n; 1230114402Sru if (has_arg() && get_number(&n, 'z', curenv->get_requested_point_size())) { 1231114402Sru if (n <= 0) 1232114402Sru n = 1; 1233114402Sru curenv->set_size(n); 1234114402Sru } 1235114402Sru else 1236114402Sru curenv->set_size(0); 1237114402Sru skip_line(); 1238114402Sru} 1239114402Sru 1240114402Sruvoid override_sizes() 1241114402Sru{ 1242114402Sru int n = 16; 1243114402Sru int *sizes = new int[n]; 1244114402Sru int i = 0; 1245114402Sru char *buf = read_string(); 1246114402Sru if (!buf) 1247114402Sru return; 1248114402Sru char *p = strtok(buf, " \t"); 1249114402Sru for (;;) { 1250114402Sru if (!p) 1251114402Sru break; 1252114402Sru int lower, upper; 1253114402Sru switch (sscanf(p, "%d-%d", &lower, &upper)) { 1254114402Sru case 1: 1255114402Sru upper = lower; 1256114402Sru // fall through 1257114402Sru case 2: 1258114402Sru if (lower <= upper && lower >= 0) 1259114402Sru break; 1260114402Sru // fall through 1261114402Sru default: 1262114402Sru warning(WARN_RANGE, "bad size range `%1'", p); 1263114402Sru return; 1264114402Sru } 1265114402Sru if (i + 2 > n) { 1266114402Sru int *old_sizes = sizes; 1267114402Sru sizes = new int[n*2]; 1268114402Sru memcpy(sizes, old_sizes, n*sizeof(int)); 1269114402Sru n *= 2; 1270114402Sru a_delete old_sizes; 1271114402Sru } 1272114402Sru sizes[i++] = lower; 1273114402Sru if (lower == 0) 1274114402Sru break; 1275114402Sru sizes[i++] = upper; 1276114402Sru p = strtok(0, " \t"); 1277114402Sru } 1278114402Sru font_size::init_size_table(sizes); 1279114402Sru} 1280114402Sru 1281114402Sruvoid space_size() 1282114402Sru{ 1283114402Sru int n; 1284114402Sru if (get_integer(&n)) { 1285114402Sru curenv->space_size = n; 1286114402Sru if (has_arg() && get_integer(&n)) 1287114402Sru curenv->sentence_space_size = n; 1288114402Sru else 1289114402Sru curenv->sentence_space_size = curenv->space_size; 1290114402Sru } 1291114402Sru skip_line(); 1292114402Sru} 1293114402Sru 1294114402Sruvoid fill() 1295114402Sru{ 1296114402Sru while (!tok.newline() && !tok.eof()) 1297114402Sru tok.next(); 1298114402Sru if (break_flag) 1299114402Sru curenv->do_break(); 1300114402Sru curenv->fill = 1; 1301114402Sru tok.next(); 1302114402Sru} 1303114402Sru 1304114402Sruvoid no_fill() 1305114402Sru{ 1306114402Sru while (!tok.newline() && !tok.eof()) 1307114402Sru tok.next(); 1308114402Sru if (break_flag) 1309114402Sru curenv->do_break(); 1310114402Sru curenv->fill = 0; 1311151497Sru curenv->suppress_next_eol = 1; 1312114402Sru tok.next(); 1313114402Sru} 1314114402Sru 1315114402Sruvoid center() 1316114402Sru{ 1317114402Sru int n; 1318114402Sru if (!has_arg() || !get_integer(&n)) 1319114402Sru n = 1; 1320114402Sru else if (n < 0) 1321114402Sru n = 0; 1322114402Sru while (!tok.newline() && !tok.eof()) 1323114402Sru tok.next(); 1324114402Sru if (break_flag) 1325114402Sru curenv->do_break(); 1326114402Sru curenv->right_justify_lines = 0; 1327114402Sru curenv->center_lines = n; 1328151497Sru curdiv->modified_tag.incl(MTSM_CE); 1329114402Sru tok.next(); 1330114402Sru} 1331114402Sru 1332114402Sruvoid right_justify() 1333114402Sru{ 1334114402Sru int n; 1335114402Sru if (!has_arg() || !get_integer(&n)) 1336114402Sru n = 1; 1337114402Sru else if (n < 0) 1338114402Sru n = 0; 1339114402Sru while (!tok.newline() && !tok.eof()) 1340114402Sru tok.next(); 1341114402Sru if (break_flag) 1342114402Sru curenv->do_break(); 1343114402Sru curenv->center_lines = 0; 1344114402Sru curenv->right_justify_lines = n; 1345151497Sru curdiv->modified_tag.incl(MTSM_RJ); 1346114402Sru tok.next(); 1347114402Sru} 1348114402Sru 1349114402Sruvoid line_length() 1350114402Sru{ 1351114402Sru hunits temp; 1352114402Sru if (has_arg() && get_hunits(&temp, 'm', curenv->line_length)) { 1353114402Sru if (temp < H0) { 1354114402Sru warning(WARN_RANGE, "bad line length %1u", temp.to_units()); 1355114402Sru temp = H0; 1356114402Sru } 1357114402Sru } 1358114402Sru else 1359114402Sru temp = curenv->prev_line_length; 1360114402Sru curenv->prev_line_length = curenv->line_length; 1361114402Sru curenv->line_length = temp; 1362151497Sru curdiv->modified_tag.incl(MTSM_LL); 1363114402Sru skip_line(); 1364114402Sru} 1365114402Sru 1366114402Sruvoid title_length() 1367114402Sru{ 1368114402Sru hunits temp; 1369114402Sru if (has_arg() && get_hunits(&temp, 'm', curenv->title_length)) { 1370114402Sru if (temp < H0) { 1371114402Sru warning(WARN_RANGE, "bad title length %1u", temp.to_units()); 1372114402Sru temp = H0; 1373114402Sru } 1374114402Sru } 1375114402Sru else 1376114402Sru temp = curenv->prev_title_length; 1377114402Sru curenv->prev_title_length = curenv->title_length; 1378114402Sru curenv->title_length = temp; 1379114402Sru skip_line(); 1380114402Sru} 1381114402Sru 1382114402Sruvoid vertical_spacing() 1383114402Sru{ 1384114402Sru vunits temp; 1385114402Sru if (has_arg() && get_vunits(&temp, 'p', curenv->vertical_spacing)) { 1386114402Sru if (temp < V0) { 1387114402Sru warning(WARN_RANGE, "vertical spacing must not be negative"); 1388114402Sru temp = vresolution; 1389114402Sru } 1390114402Sru } 1391114402Sru else 1392114402Sru temp = curenv->prev_vertical_spacing; 1393114402Sru curenv->prev_vertical_spacing = curenv->vertical_spacing; 1394114402Sru curenv->vertical_spacing = temp; 1395114402Sru skip_line(); 1396114402Sru} 1397114402Sru 1398114402Sruvoid post_vertical_spacing() 1399114402Sru{ 1400114402Sru vunits temp; 1401114402Sru if (has_arg() && get_vunits(&temp, 'p', curenv->post_vertical_spacing)) { 1402114402Sru if (temp < V0) { 1403114402Sru warning(WARN_RANGE, 1404114402Sru "post vertical spacing must be greater than or equal to 0"); 1405114402Sru temp = V0; 1406114402Sru } 1407114402Sru } 1408114402Sru else 1409114402Sru temp = curenv->prev_post_vertical_spacing; 1410114402Sru curenv->prev_post_vertical_spacing = curenv->post_vertical_spacing; 1411114402Sru curenv->post_vertical_spacing = temp; 1412114402Sru skip_line(); 1413114402Sru} 1414114402Sru 1415114402Sruvoid line_spacing() 1416114402Sru{ 1417114402Sru int temp; 1418114402Sru if (has_arg() && get_integer(&temp)) { 1419114402Sru if (temp < 1) { 1420114402Sru warning(WARN_RANGE, "value %1 out of range: interpreted as 1", temp); 1421114402Sru temp = 1; 1422114402Sru } 1423114402Sru } 1424114402Sru else 1425114402Sru temp = curenv->prev_line_spacing; 1426114402Sru curenv->prev_line_spacing = curenv->line_spacing; 1427114402Sru curenv->line_spacing = temp; 1428114402Sru skip_line(); 1429114402Sru} 1430114402Sru 1431114402Sruvoid indent() 1432114402Sru{ 1433114402Sru hunits temp; 1434114402Sru if (has_arg() && get_hunits(&temp, 'm', curenv->indent)) { 1435114402Sru if (temp < H0) { 1436114402Sru warning(WARN_RANGE, "indent cannot be negative"); 1437114402Sru temp = H0; 1438114402Sru } 1439114402Sru } 1440114402Sru else 1441114402Sru temp = curenv->prev_indent; 1442114402Sru while (!tok.newline() && !tok.eof()) 1443114402Sru tok.next(); 1444114402Sru if (break_flag) 1445114402Sru curenv->do_break(); 1446114402Sru curenv->have_temporary_indent = 0; 1447114402Sru curenv->prev_indent = curenv->indent; 1448114402Sru curenv->indent = temp; 1449151497Sru curdiv->modified_tag.incl(MTSM_IN); 1450114402Sru tok.next(); 1451114402Sru} 1452114402Sru 1453114402Sruvoid temporary_indent() 1454114402Sru{ 1455114402Sru int err = 0; 1456114402Sru hunits temp; 1457114402Sru if (!get_hunits(&temp, 'm', curenv->get_indent())) 1458114402Sru err = 1; 1459114402Sru while (!tok.newline() && !tok.eof()) 1460114402Sru tok.next(); 1461114402Sru if (break_flag) 1462114402Sru curenv->do_break(); 1463114402Sru if (temp < H0) { 1464114402Sru warning(WARN_RANGE, "total indent cannot be negative"); 1465114402Sru temp = H0; 1466114402Sru } 1467114402Sru if (!err) { 1468114402Sru curenv->temporary_indent = temp; 1469114402Sru curenv->have_temporary_indent = 1; 1470151497Sru curdiv->modified_tag.incl(MTSM_TI); 1471114402Sru } 1472114402Sru tok.next(); 1473114402Sru} 1474114402Sru 1475114402Srunode *do_underline_special(int underline_spaces) 1476114402Sru{ 1477114402Sru macro m; 1478114402Sru m.append_str("x u "); 1479114402Sru m.append(underline_spaces + '0'); 1480114402Sru return new special_node(m, 1); 1481114402Sru} 1482114402Sru 1483114402Sruvoid do_underline(int underline_spaces) 1484114402Sru{ 1485114402Sru int n; 1486114402Sru if (!has_arg() || !get_integer(&n)) 1487114402Sru n = 1; 1488114402Sru if (n <= 0) { 1489114402Sru if (curenv->underline_lines > 0) { 1490114402Sru curenv->prev_fontno = curenv->fontno; 1491114402Sru curenv->fontno = curenv->pre_underline_fontno; 1492114402Sru if (underline_spaces) { 1493114402Sru curenv->underline_spaces = 0; 1494114402Sru curenv->add_node(do_underline_special(0)); 1495114402Sru } 1496114402Sru } 1497114402Sru curenv->underline_lines = 0; 1498114402Sru } 1499114402Sru else { 1500114402Sru curenv->underline_lines = n; 1501114402Sru curenv->pre_underline_fontno = curenv->fontno; 1502114402Sru curenv->fontno = get_underline_fontno(); 1503114402Sru if (underline_spaces) { 1504114402Sru curenv->underline_spaces = 1; 1505114402Sru curenv->add_node(do_underline_special(1)); 1506114402Sru } 1507114402Sru } 1508114402Sru skip_line(); 1509114402Sru} 1510114402Sru 1511114402Sruvoid continuous_underline() 1512114402Sru{ 1513114402Sru do_underline(1); 1514114402Sru} 1515114402Sru 1516114402Sruvoid underline() 1517114402Sru{ 1518114402Sru do_underline(0); 1519114402Sru} 1520114402Sru 1521114402Sruvoid control_char() 1522114402Sru{ 1523114402Sru curenv->control_char = '.'; 1524114402Sru if (has_arg()) { 1525114402Sru if (tok.ch() == 0) 1526114402Sru error("bad control character"); 1527114402Sru else 1528114402Sru curenv->control_char = tok.ch(); 1529114402Sru } 1530114402Sru skip_line(); 1531114402Sru} 1532114402Sru 1533114402Sruvoid no_break_control_char() 1534114402Sru{ 1535114402Sru curenv->no_break_control_char = '\''; 1536114402Sru if (has_arg()) { 1537114402Sru if (tok.ch() == 0) 1538114402Sru error("bad control character"); 1539114402Sru else 1540114402Sru curenv->no_break_control_char = tok.ch(); 1541114402Sru } 1542114402Sru skip_line(); 1543114402Sru} 1544114402Sru 1545114402Sruvoid margin_character() 1546114402Sru{ 1547114402Sru while (tok.space()) 1548114402Sru tok.next(); 1549114402Sru charinfo *ci = tok.get_char(); 1550114402Sru if (ci) { 1551114402Sru // Call tok.next() only after making the node so that 1552114402Sru // .mc \s+9\(br\s0 works. 1553114402Sru node *nd = curenv->make_char_node(ci); 1554114402Sru tok.next(); 1555114402Sru if (nd) { 1556114402Sru delete curenv->margin_character_node; 1557114402Sru curenv->margin_character_node = nd; 1558114402Sru curenv->margin_character_flags = (MARGIN_CHARACTER_ON 1559114402Sru |MARGIN_CHARACTER_NEXT); 1560114402Sru hunits d; 1561114402Sru if (has_arg() && get_hunits(&d, 'm')) 1562114402Sru curenv->margin_character_distance = d; 1563114402Sru } 1564114402Sru } 1565114402Sru else { 1566114402Sru check_missing_character(); 1567114402Sru curenv->margin_character_flags &= ~MARGIN_CHARACTER_ON; 1568114402Sru if (curenv->margin_character_flags == 0) { 1569114402Sru delete curenv->margin_character_node; 1570114402Sru curenv->margin_character_node = 0; 1571114402Sru } 1572114402Sru } 1573114402Sru skip_line(); 1574114402Sru} 1575114402Sru 1576114402Sruvoid number_lines() 1577114402Sru{ 1578114402Sru delete_node_list(curenv->numbering_nodes); 1579114402Sru curenv->numbering_nodes = 0; 1580114402Sru if (has_arg()) { 1581114402Sru node *nd = 0; 1582114402Sru for (int i = '9'; i >= '0'; i--) { 1583114402Sru node *tem = make_node(charset_table[i], curenv); 1584114402Sru if (!tem) { 1585114402Sru skip_line(); 1586114402Sru return; 1587114402Sru } 1588114402Sru tem->next = nd; 1589114402Sru nd = tem; 1590114402Sru } 1591114402Sru curenv->numbering_nodes = nd; 1592114402Sru curenv->line_number_digit_width = env_digit_width(curenv); 1593114402Sru int n; 1594114402Sru if (!tok.delimiter()) { 1595114402Sru if (get_integer(&n, next_line_number)) { 1596114402Sru next_line_number = n; 1597114402Sru if (next_line_number < 0) { 1598114402Sru warning(WARN_RANGE, "negative line number"); 1599114402Sru next_line_number = 0; 1600114402Sru } 1601114402Sru } 1602114402Sru } 1603114402Sru else 1604114402Sru while (!tok.space() && !tok.newline() && !tok.eof()) 1605114402Sru tok.next(); 1606114402Sru if (has_arg()) { 1607114402Sru if (!tok.delimiter()) { 1608114402Sru if (get_integer(&n)) { 1609114402Sru if (n <= 0) { 1610114402Sru warning(WARN_RANGE, "negative or zero line number multiple"); 1611114402Sru } 1612114402Sru else 1613114402Sru curenv->line_number_multiple = n; 1614114402Sru } 1615114402Sru } 1616114402Sru else 1617114402Sru while (!tok.space() && !tok.newline() && !tok.eof()) 1618114402Sru tok.next(); 1619114402Sru if (has_arg()) { 1620114402Sru if (!tok.delimiter()) { 1621114402Sru if (get_integer(&n)) 1622114402Sru curenv->number_text_separation = n; 1623114402Sru } 1624114402Sru else 1625114402Sru while (!tok.space() && !tok.newline() && !tok.eof()) 1626114402Sru tok.next(); 1627114402Sru if (has_arg() && !tok.delimiter() && get_integer(&n)) 1628114402Sru curenv->line_number_indent = n; 1629114402Sru } 1630114402Sru } 1631114402Sru } 1632114402Sru skip_line(); 1633114402Sru} 1634114402Sru 1635114402Sruvoid no_number() 1636114402Sru{ 1637114402Sru int n; 1638114402Sru if (has_arg() && get_integer(&n)) 1639114402Sru curenv->no_number_count = n > 0 ? n : 0; 1640114402Sru else 1641114402Sru curenv->no_number_count = 1; 1642114402Sru skip_line(); 1643114402Sru} 1644114402Sru 1645114402Sruvoid no_hyphenate() 1646114402Sru{ 1647114402Sru curenv->hyphenation_flags = 0; 1648114402Sru skip_line(); 1649114402Sru} 1650114402Sru 1651114402Sruvoid hyphenate_request() 1652114402Sru{ 1653114402Sru int n; 1654114402Sru if (has_arg() && get_integer(&n)) 1655114402Sru curenv->hyphenation_flags = n; 1656114402Sru else 1657114402Sru curenv->hyphenation_flags = 1; 1658114402Sru skip_line(); 1659114402Sru} 1660114402Sru 1661114402Sruvoid hyphen_char() 1662114402Sru{ 1663114402Sru curenv->hyphen_indicator_char = get_optional_char(); 1664114402Sru skip_line(); 1665114402Sru} 1666114402Sru 1667114402Sruvoid hyphen_line_max_request() 1668114402Sru{ 1669114402Sru int n; 1670114402Sru if (has_arg() && get_integer(&n)) 1671114402Sru curenv->hyphen_line_max = n; 1672114402Sru else 1673114402Sru curenv->hyphen_line_max = -1; 1674114402Sru skip_line(); 1675114402Sru} 1676114402Sru 1677114402Sruvoid environment::interrupt() 1678114402Sru{ 1679114402Sru if (!dummy) { 1680114402Sru add_node(new transparent_dummy_node); 1681114402Sru interrupted = 1; 1682114402Sru } 1683114402Sru} 1684114402Sru 1685114402Sruvoid environment::newline() 1686114402Sru{ 1687151497Sru int was_centered = 0; 1688114402Sru if (underline_lines > 0) { 1689114402Sru if (--underline_lines == 0) { 1690114402Sru prev_fontno = fontno; 1691114402Sru fontno = pre_underline_fontno; 1692114402Sru if (underline_spaces) { 1693114402Sru underline_spaces = 0; 1694114402Sru add_node(do_underline_special(0)); 1695114402Sru } 1696114402Sru } 1697114402Sru } 1698114402Sru if (current_field) 1699114402Sru wrap_up_field(); 1700114402Sru if (current_tab) 1701114402Sru wrap_up_tab(); 1702114402Sru // strip trailing spaces 1703114402Sru while (line != 0 && line->discardable()) { 1704114402Sru width_total -= line->width(); 1705114402Sru space_total -= line->nspaces(); 1706114402Sru node *tem = line; 1707114402Sru line = line->next; 1708114402Sru delete tem; 1709114402Sru } 1710114402Sru node *to_be_output = 0; 1711114402Sru hunits to_be_output_width; 1712114402Sru prev_line_interrupted = 0; 1713114402Sru if (dummy) 1714114402Sru space_newline(); 1715114402Sru else if (interrupted) { 1716114402Sru interrupted = 0; 1717114402Sru // see environment::final_break 1718114402Sru prev_line_interrupted = exit_started ? 2 : 1; 1719114402Sru } 1720114402Sru else if (center_lines > 0) { 1721114402Sru --center_lines; 1722114402Sru hunits x = target_text_length - width_total; 1723114402Sru if (x > H0) 1724114402Sru saved_indent += x/2; 1725114402Sru to_be_output = line; 1726151497Sru was_centered = 1; 1727114402Sru to_be_output_width = width_total; 1728114402Sru line = 0; 1729114402Sru } 1730114402Sru else if (right_justify_lines > 0) { 1731114402Sru --right_justify_lines; 1732114402Sru hunits x = target_text_length - width_total; 1733114402Sru if (x > H0) 1734114402Sru saved_indent += x; 1735114402Sru to_be_output = line; 1736114402Sru to_be_output_width = width_total; 1737114402Sru line = 0; 1738114402Sru } 1739114402Sru else if (fill) 1740114402Sru space_newline(); 1741114402Sru else { 1742114402Sru to_be_output = line; 1743114402Sru to_be_output_width = width_total; 1744114402Sru line = 0; 1745114402Sru } 1746114402Sru input_line_start = line == 0 ? H0 : width_total; 1747114402Sru if (to_be_output) { 1748114402Sru if (is_html && !fill) { 1749151497Sru curdiv->modified_tag.incl(MTSM_EOL); 1750151497Sru if (suppress_next_eol) 1751151497Sru suppress_next_eol = 0; 1752151497Sru else 1753151497Sru seen_eol = 1; 1754151497Sru } 1755114402Sru 1756151497Sru output_line(to_be_output, to_be_output_width, was_centered); 1757114402Sru hyphen_line_count = 0; 1758114402Sru } 1759114402Sru if (input_trap_count > 0) { 1760114402Sru if (!(continued_input_trap && prev_line_interrupted)) 1761114402Sru if (--input_trap_count == 0) 1762114402Sru spring_trap(input_trap); 1763114402Sru } 1764114402Sru} 1765114402Sru 1766151497Sruvoid environment::output_line(node *n, hunits width, int was_centered) 1767114402Sru{ 1768114402Sru prev_text_length = width; 1769114402Sru if (margin_character_flags) { 1770114402Sru hunits d = line_length + margin_character_distance - saved_indent - width; 1771114402Sru if (d > 0) { 1772114402Sru n = new hmotion_node(d, get_fill_color(), n); 1773114402Sru width += d; 1774114402Sru } 1775114402Sru margin_character_flags &= ~MARGIN_CHARACTER_NEXT; 1776114402Sru node *tem; 1777114402Sru if (!margin_character_flags) { 1778114402Sru tem = margin_character_node; 1779114402Sru margin_character_node = 0; 1780114402Sru } 1781114402Sru else 1782114402Sru tem = margin_character_node->copy(); 1783114402Sru tem->next = n; 1784114402Sru n = tem; 1785114402Sru width += tem->width(); 1786114402Sru } 1787114402Sru node *nn = 0; 1788114402Sru while (n != 0) { 1789114402Sru node *tem = n->next; 1790114402Sru n->next = nn; 1791114402Sru nn = n; 1792114402Sru n = tem; 1793114402Sru } 1794114402Sru if (!saved_indent.is_zero()) 1795114402Sru nn = new hmotion_node(saved_indent, get_fill_color(), nn); 1796114402Sru width += saved_indent; 1797114402Sru if (no_number_count > 0) 1798114402Sru --no_number_count; 1799114402Sru else if (numbering_nodes) { 1800114402Sru hunits w = (line_number_digit_width 1801114402Sru *(3+line_number_indent+number_text_separation)); 1802114402Sru if (next_line_number % line_number_multiple != 0) 1803114402Sru nn = new hmotion_node(w, get_fill_color(), nn); 1804114402Sru else { 1805114402Sru hunits x = w; 1806114402Sru nn = new hmotion_node(number_text_separation * line_number_digit_width, 1807114402Sru get_fill_color(), nn); 1808114402Sru x -= number_text_separation*line_number_digit_width; 1809114402Sru char buf[30]; 1810114402Sru sprintf(buf, "%3d", next_line_number); 1811114402Sru for (char *p = strchr(buf, '\0') - 1; p >= buf && *p != ' '; --p) { 1812114402Sru node *gn = numbering_nodes; 1813114402Sru for (int count = *p - '0'; count > 0; count--) 1814114402Sru gn = gn->next; 1815114402Sru gn = gn->copy(); 1816114402Sru x -= gn->width(); 1817114402Sru gn->next = nn; 1818114402Sru nn = gn; 1819114402Sru } 1820114402Sru nn = new hmotion_node(x, get_fill_color(), nn); 1821114402Sru } 1822114402Sru width += w; 1823114402Sru ++next_line_number; 1824114402Sru } 1825151497Sru output(nn, !fill, vertical_spacing, total_post_vertical_spacing(), width, 1826151497Sru was_centered); 1827114402Sru} 1828114402Sru 1829114402Sruvoid environment::start_line() 1830114402Sru{ 1831114402Sru assert(line == 0); 1832114402Sru discarding = 0; 1833114402Sru line = new line_start_node; 1834114402Sru if (have_temporary_indent) { 1835114402Sru saved_indent = temporary_indent; 1836114402Sru have_temporary_indent = 0; 1837114402Sru } 1838114402Sru else 1839114402Sru saved_indent = indent; 1840114402Sru target_text_length = line_length - saved_indent; 1841114402Sru width_total = H0; 1842114402Sru space_total = 0; 1843114402Sru} 1844114402Sru 1845114402Sruhunits environment::get_hyphenation_space() 1846114402Sru{ 1847114402Sru return hyphenation_space; 1848114402Sru} 1849114402Sru 1850114402Sruvoid hyphenation_space_request() 1851114402Sru{ 1852114402Sru hunits n; 1853114402Sru if (get_hunits(&n, 'm')) { 1854114402Sru if (n < H0) { 1855114402Sru warning(WARN_RANGE, "hyphenation space cannot be negative"); 1856114402Sru n = H0; 1857114402Sru } 1858114402Sru curenv->hyphenation_space = n; 1859114402Sru } 1860114402Sru skip_line(); 1861114402Sru} 1862114402Sru 1863114402Sruhunits environment::get_hyphenation_margin() 1864114402Sru{ 1865114402Sru return hyphenation_margin; 1866114402Sru} 1867114402Sru 1868114402Sruvoid hyphenation_margin_request() 1869114402Sru{ 1870114402Sru hunits n; 1871114402Sru if (get_hunits(&n, 'm')) { 1872114402Sru if (n < H0) { 1873114402Sru warning(WARN_RANGE, "hyphenation margin cannot be negative"); 1874114402Sru n = H0; 1875114402Sru } 1876114402Sru curenv->hyphenation_margin = n; 1877114402Sru } 1878114402Sru skip_line(); 1879114402Sru} 1880114402Sru 1881114402Srubreakpoint *environment::choose_breakpoint() 1882114402Sru{ 1883114402Sru hunits x = width_total; 1884114402Sru int s = space_total; 1885114402Sru node *n = line; 1886114402Sru breakpoint *best_bp = 0; // the best breakpoint so far 1887114402Sru int best_bp_fits = 0; 1888114402Sru while (n != 0) { 1889114402Sru x -= n->width(); 1890114402Sru s -= n->nspaces(); 1891114402Sru breakpoint *bp = n->get_breakpoints(x, s); 1892114402Sru while (bp != 0) { 1893114402Sru if (bp->width <= target_text_length) { 1894114402Sru if (!bp->hyphenated) { 1895114402Sru breakpoint *tem = bp->next; 1896114402Sru bp->next = 0; 1897114402Sru while (tem != 0) { 1898114402Sru breakpoint *tem1 = tem; 1899114402Sru tem = tem->next; 1900114402Sru delete tem1; 1901114402Sru } 1902114402Sru if (best_bp_fits 1903114402Sru // Decide whether to use the hyphenated breakpoint. 1904114402Sru && (hyphen_line_max < 0 1905114402Sru // Only choose the hyphenated breakpoint if it would not 1906114402Sru // exceed the maximum number of consecutive hyphenated 1907114402Sru // lines. 1908114402Sru || hyphen_line_count + 1 <= hyphen_line_max) 1909114402Sru && !(adjust_mode == ADJUST_BOTH 1910114402Sru // Don't choose the hyphenated breakpoint if the line 1911114402Sru // can be justified by adding no more than 1912114402Sru // hyphenation_space to any word space. 1913114402Sru ? (bp->nspaces > 0 1914114402Sru && (((target_text_length - bp->width 1915114402Sru + (bp->nspaces - 1)*hresolution)/bp->nspaces) 1916114402Sru <= hyphenation_space)) 1917114402Sru // Don't choose the hyphenated breakpoint if the line 1918114402Sru // is no more than hyphenation_margin short. 1919114402Sru : target_text_length - bp->width <= hyphenation_margin)) { 1920114402Sru delete bp; 1921114402Sru return best_bp; 1922114402Sru } 1923114402Sru if (best_bp) 1924114402Sru delete best_bp; 1925114402Sru return bp; 1926114402Sru } 1927114402Sru else { 1928114402Sru if ((adjust_mode == ADJUST_BOTH 1929114402Sru ? hyphenation_space == H0 1930114402Sru : hyphenation_margin == H0) 1931114402Sru && (hyphen_line_max < 0 1932114402Sru || hyphen_line_count + 1 <= hyphen_line_max)) { 1933114402Sru // No need to consider a non-hyphenated breakpoint. 1934114402Sru if (best_bp) 1935114402Sru delete best_bp; 1936114402Sru breakpoint *tem = bp->next; 1937114402Sru bp->next = 0; 1938114402Sru while (tem != 0) { 1939114402Sru breakpoint *tem1 = tem; 1940114402Sru tem = tem->next; 1941114402Sru delete tem1; 1942114402Sru } 1943114402Sru return bp; 1944114402Sru } 1945114402Sru // It fits but it's hyphenated. 1946114402Sru if (!best_bp_fits) { 1947114402Sru if (best_bp) 1948114402Sru delete best_bp; 1949114402Sru best_bp = bp; 1950114402Sru bp = bp->next; 1951114402Sru best_bp_fits = 1; 1952114402Sru } 1953114402Sru else { 1954114402Sru breakpoint *tem = bp; 1955114402Sru bp = bp->next; 1956114402Sru delete tem; 1957114402Sru } 1958114402Sru } 1959114402Sru } 1960114402Sru else { 1961114402Sru if (best_bp) 1962114402Sru delete best_bp; 1963114402Sru best_bp = bp; 1964114402Sru bp = bp->next; 1965114402Sru } 1966114402Sru } 1967114402Sru n = n->next; 1968114402Sru } 1969114402Sru if (best_bp) { 1970114402Sru if (!best_bp_fits) 1971114402Sru output_warning(WARN_BREAK, "can't break line"); 1972114402Sru return best_bp; 1973114402Sru } 1974114402Sru return 0; 1975114402Sru} 1976114402Sru 1977114402Sruvoid environment::hyphenate_line(int start_here) 1978114402Sru{ 1979114402Sru assert(line != 0); 1980114402Sru hyphenation_type prev_type = line->get_hyphenation_type(); 1981114402Sru node **startp; 1982114402Sru if (start_here) 1983114402Sru startp = &line; 1984114402Sru else 1985114402Sru for (startp = &line->next; *startp != 0; startp = &(*startp)->next) { 1986114402Sru hyphenation_type this_type = (*startp)->get_hyphenation_type(); 1987114402Sru if (prev_type == HYPHEN_BOUNDARY && this_type == HYPHEN_MIDDLE) 1988114402Sru break; 1989114402Sru prev_type = this_type; 1990114402Sru } 1991114402Sru if (*startp == 0) 1992114402Sru return; 1993114402Sru node *tem = *startp; 1994114402Sru do { 1995114402Sru tem = tem->next; 1996114402Sru } while (tem != 0 && tem->get_hyphenation_type() == HYPHEN_MIDDLE); 1997114402Sru int inhibit = (tem != 0 && tem->get_hyphenation_type() == HYPHEN_INHIBIT); 1998114402Sru node *end = tem; 1999114402Sru hyphen_list *sl = 0; 2000114402Sru tem = *startp; 2001114402Sru node *forward = 0; 2002114402Sru int i = 0; 2003114402Sru while (tem != end) { 2004114402Sru sl = tem->get_hyphen_list(sl, &i); 2005114402Sru node *tem1 = tem; 2006114402Sru tem = tem->next; 2007114402Sru tem1->next = forward; 2008114402Sru forward = tem1; 2009114402Sru } 2010114402Sru if (!inhibit) { 2011114402Sru // this is for characters like hyphen and emdash 2012114402Sru int prev_code = 0; 2013114402Sru for (hyphen_list *h = sl; h; h = h->next) { 2014114402Sru h->breakable = (prev_code != 0 2015114402Sru && h->next != 0 2016114402Sru && h->next->hyphenation_code != 0); 2017114402Sru prev_code = h->hyphenation_code; 2018114402Sru } 2019114402Sru } 2020114402Sru if (hyphenation_flags != 0 2021114402Sru && !inhibit 2022114402Sru // this may not be right if we have extra space on this line 2023114402Sru && !((hyphenation_flags & HYPHEN_LAST_LINE) 2024114402Sru && (curdiv->distance_to_next_trap() 2025114402Sru <= vertical_spacing + total_post_vertical_spacing())) 2026114402Sru && i >= 4) 2027114402Sru hyphenate(sl, hyphenation_flags); 2028114402Sru while (forward != 0) { 2029114402Sru node *tem1 = forward; 2030114402Sru forward = forward->next; 2031114402Sru tem1->next = 0; 2032114402Sru tem = tem1->add_self(tem, &sl); 2033114402Sru } 2034114402Sru *startp = tem; 2035114402Sru} 2036114402Sru 2037114402Srustatic node *node_list_reverse(node *n) 2038114402Sru{ 2039114402Sru node *res = 0; 2040114402Sru while (n) { 2041114402Sru node *tem = n; 2042114402Sru n = n->next; 2043114402Sru tem->next = res; 2044114402Sru res = tem; 2045114402Sru } 2046114402Sru return res; 2047114402Sru} 2048114402Sru 2049114402Srustatic void distribute_space(node *n, int nspaces, hunits desired_space, 2050114402Sru int force_reverse = 0) 2051114402Sru{ 2052114402Sru static int reverse = 0; 2053114402Sru if (force_reverse || reverse) 2054114402Sru n = node_list_reverse(n); 2055114402Sru if (!force_reverse && nspaces > 0 && spread_limit >= 0 2056114402Sru && desired_space.to_units() > 0) { 2057114402Sru hunits em = curenv->get_size(); 2058114402Sru double Ems = (double)desired_space.to_units() / nspaces 2059114402Sru / (em.is_zero() ? hresolution : em.to_units()); 2060114402Sru if (Ems > spread_limit) 2061114402Sru output_warning(WARN_BREAK, "spreading %1m per space", Ems); 2062114402Sru } 2063114402Sru for (node *tem = n; tem; tem = tem->next) 2064114402Sru tem->spread_space(&nspaces, &desired_space); 2065114402Sru if (force_reverse || reverse) 2066114402Sru (void)node_list_reverse(n); 2067114402Sru if (!force_reverse) 2068114402Sru reverse = !reverse; 2069114402Sru assert(desired_space.is_zero() && nspaces == 0); 2070114402Sru} 2071114402Sru 2072114402Sruvoid environment::possibly_break_line(int start_here, int forced) 2073114402Sru{ 2074151497Sru int was_centered = center_lines > 0; 2075114402Sru if (!fill || current_tab || current_field || dummy) 2076114402Sru return; 2077114402Sru while (line != 0 2078114402Sru && (forced 2079114402Sru // When a macro follows a paragraph in fill mode, the 2080114402Sru // current line should not be empty. 2081114402Sru || (width_total - line->width()) > target_text_length)) { 2082114402Sru hyphenate_line(start_here); 2083114402Sru breakpoint *bp = choose_breakpoint(); 2084114402Sru if (bp == 0) 2085114402Sru // we'll find one eventually 2086114402Sru return; 2087114402Sru node *pre, *post; 2088114402Sru node **ndp = &line; 2089114402Sru while (*ndp != bp->nd) 2090114402Sru ndp = &(*ndp)->next; 2091114402Sru bp->nd->split(bp->index, &pre, &post); 2092114402Sru *ndp = post; 2093114402Sru hunits extra_space_width = H0; 2094114402Sru switch(adjust_mode) { 2095114402Sru case ADJUST_BOTH: 2096114402Sru if (bp->nspaces != 0) 2097114402Sru extra_space_width = target_text_length - bp->width; 2098114402Sru else if (bp->width > 0 && target_text_length > 0 2099114402Sru && target_text_length > bp->width) 2100114402Sru output_warning(WARN_BREAK, "cannot adjust line"); 2101114402Sru break; 2102114402Sru case ADJUST_CENTER: 2103114402Sru saved_indent += (target_text_length - bp->width)/2; 2104151497Sru was_centered = 1; 2105114402Sru break; 2106114402Sru case ADJUST_RIGHT: 2107114402Sru saved_indent += target_text_length - bp->width; 2108114402Sru break; 2109114402Sru } 2110114402Sru distribute_space(pre, bp->nspaces, extra_space_width); 2111114402Sru hunits output_width = bp->width + extra_space_width; 2112114402Sru input_line_start -= output_width; 2113114402Sru if (bp->hyphenated) 2114114402Sru hyphen_line_count++; 2115114402Sru else 2116114402Sru hyphen_line_count = 0; 2117114402Sru delete bp; 2118114402Sru space_total = 0; 2119114402Sru width_total = 0; 2120114402Sru node *first_non_discardable = 0; 2121114402Sru node *tem; 2122114402Sru for (tem = line; tem != 0; tem = tem->next) 2123114402Sru if (!tem->discardable()) 2124114402Sru first_non_discardable = tem; 2125114402Sru node *to_be_discarded; 2126114402Sru if (first_non_discardable) { 2127114402Sru to_be_discarded = first_non_discardable->next; 2128114402Sru first_non_discardable->next = 0; 2129114402Sru for (tem = line; tem != 0; tem = tem->next) { 2130114402Sru width_total += tem->width(); 2131114402Sru space_total += tem->nspaces(); 2132114402Sru } 2133114402Sru discarding = 0; 2134114402Sru } 2135114402Sru else { 2136114402Sru discarding = 1; 2137114402Sru to_be_discarded = line; 2138114402Sru line = 0; 2139114402Sru } 2140114402Sru // Do output_line() here so that line will be 0 iff the 2141114402Sru // the environment will be empty. 2142151497Sru output_line(pre, output_width, was_centered); 2143114402Sru while (to_be_discarded != 0) { 2144114402Sru tem = to_be_discarded; 2145114402Sru to_be_discarded = to_be_discarded->next; 2146114402Sru input_line_start -= tem->width(); 2147114402Sru delete tem; 2148114402Sru } 2149114402Sru if (line != 0) { 2150114402Sru if (have_temporary_indent) { 2151114402Sru saved_indent = temporary_indent; 2152114402Sru have_temporary_indent = 0; 2153114402Sru } 2154114402Sru else 2155114402Sru saved_indent = indent; 2156114402Sru target_text_length = line_length - saved_indent; 2157114402Sru } 2158114402Sru } 2159114402Sru} 2160114402Sru 2161114402Sru/* 2162114402SruDo the break at the end of input after the end macro (if any). 2163114402Sru 2164114402SruUnix troff behaves as follows: if the last line is 2165114402Sru 2166114402Srufoo bar\c 2167114402Sru 2168114402Sruit will output foo on the current page, and bar on the next page; 2169114402Sruif the last line is 2170114402Sru 2171114402Srufoo\c 2172114402Sru 2173114402Sruor 2174114402Sru 2175114402Srufoo bar 2176114402Sru 2177114402Srueverything will be output on the current page. This behaviour must be 2178114402Sruconsidered a bug. 2179114402Sru 2180114402SruThe problem is that some macro packages rely on this. For example, 2181114402Sruthe ATK macros have an end macro that emits \c if it needs to print a 2182114402Srutable of contents but doesn't do a 'bp in the end macro; instead the 2183114402Sru'bp is done in the bottom of page trap. This works with Unix troff, 2184114402Sruprovided that the current environment is not empty at the end of the 2185114402Sruinput file. 2186114402Sru 2187114402SruThe following will make macro packages that do that sort of thing work 2188114402Srueven if the current environment is empty at the end of the input file. 2189114402SruIf the last input line used \c and this line occurred in the end macro, 2190114402Sruthen we'll force everything out on the current page, but we'll make 2191114402Srusure that the environment isn't empty so that we won't exit at the 2192114402Srubottom of this page. 2193114402Sru*/ 2194114402Sru 2195114402Sruvoid environment::final_break() 2196114402Sru{ 2197114402Sru if (prev_line_interrupted == 2) { 2198114402Sru do_break(); 2199114402Sru add_node(new transparent_dummy_node); 2200114402Sru } 2201114402Sru else 2202114402Sru do_break(); 2203114402Sru} 2204114402Sru 2205151497Srunode *environment::make_tag(const char *nm, int i) 2206114402Sru{ 2207114402Sru if (is_html) { 2208114402Sru /* 2209114402Sru * need to emit tag for post-grohtml 2210114402Sru * but we check to see whether we can emit specials 2211114402Sru */ 2212114402Sru if (curdiv == topdiv && topdiv->before_first_page) 2213114402Sru topdiv->begin_page(); 2214114402Sru macro *m = new macro; 2215151497Sru m->append_str("devtag:"); 2216151497Sru for (const char *p = nm; *p; p++) 2217114402Sru if (!invalid_input_char((unsigned char)*p)) 2218114402Sru m->append(*p); 2219151497Sru m->append(' '); 2220151497Sru m->append_int(i); 2221151497Sru return new special_node(*m); 2222114402Sru } 2223151497Sru return 0; 2224114402Sru} 2225114402Sru 2226151497Sruvoid environment::dump_troff_state() 2227114402Sru{ 2228151497Sru#define SPACES " " 2229151497Sru fprintf(stderr, SPACES "register `in' = %d\n", curenv->indent.to_units()); 2230151497Sru if (curenv->have_temporary_indent) 2231151497Sru fprintf(stderr, SPACES "register `ti' = %d\n", 2232151497Sru curenv->temporary_indent.to_units()); 2233151497Sru fprintf(stderr, SPACES "centered lines `ce' = %d\n", curenv->center_lines); 2234151497Sru fprintf(stderr, SPACES "register `ll' = %d\n", 2235151497Sru curenv->line_length.to_units()); 2236151497Sru fprintf(stderr, SPACES "fill `fi=1/nf=0' = %d\n", curenv->fill); 2237151497Sru fprintf(stderr, SPACES "page offset `po' = %d\n", 2238151497Sru topdiv->get_page_offset().to_units()); 2239151497Sru fprintf(stderr, SPACES "seen_break = %d\n", curenv->seen_break); 2240151497Sru fprintf(stderr, SPACES "seen_space = %d\n", curenv->seen_space); 2241151497Sru fflush(stderr); 2242151497Sru#undef SPACES 2243114402Sru} 2244114402Sru 2245151497Srustatem *environment::construct_state(int only_eol) 2246114402Sru{ 2247114402Sru if (is_html) { 2248151497Sru statem *s = new statem(); 2249151497Sru if (!only_eol) { 2250151497Sru s->add_tag(MTSM_IN, indent); 2251151497Sru s->add_tag(MTSM_LL, line_length); 2252151497Sru s->add_tag(MTSM_PO, topdiv->get_page_offset().to_units()); 2253151497Sru s->add_tag(MTSM_RJ, right_justify_lines); 2254151497Sru if (have_temporary_indent) 2255151497Sru s->add_tag(MTSM_TI, temporary_indent); 2256151497Sru s->add_tag_ta(); 2257151497Sru if (seen_break) 2258151497Sru s->add_tag(MTSM_BR); 2259151497Sru if (seen_space != 0) 2260151497Sru s->add_tag(MTSM_SP, seen_space); 2261151497Sru seen_break = 0; 2262151497Sru seen_space = 0; 2263151497Sru } 2264151497Sru if (seen_eol) { 2265151497Sru s->add_tag(MTSM_EOL); 2266151497Sru s->add_tag(MTSM_CE, center_lines); 2267151497Sru } 2268151497Sru seen_eol = 0; 2269151497Sru return s; 2270114402Sru } 2271151497Sru else 2272151497Sru return NULL; 2273114402Sru} 2274114402Sru 2275151497Sruvoid environment::construct_format_state(node *n, int was_centered, 2276151497Sru int filling) 2277114402Sru{ 2278114402Sru if (is_html) { 2279151497Sru // find first glyph node which has a state. 2280151497Sru while (n != 0 && n->state == 0) 2281151497Sru n = n->next; 2282151497Sru if (n == 0 || (n->state == 0)) 2283151497Sru return; 2284151497Sru if (seen_space != 0) 2285151497Sru n->state->add_tag(MTSM_SP, seen_space); 2286151497Sru if (seen_eol && topdiv == curdiv) 2287151497Sru n->state->add_tag(MTSM_EOL); 2288151497Sru seen_space = 0; 2289151497Sru seen_eol = 0; 2290151497Sru if (was_centered) 2291151497Sru n->state->add_tag(MTSM_CE, center_lines+1); 2292151497Sru else 2293151497Sru n->state->add_tag_if_unknown(MTSM_CE, 0); 2294151497Sru n->state->add_tag_if_unknown(MTSM_FI, filling); 2295151497Sru n = n->next; 2296151497Sru while (n != 0) { 2297151497Sru if (n->state != 0) { 2298151497Sru n->state->sub_tag_ce(); 2299151497Sru n->state->add_tag_if_unknown(MTSM_FI, filling); 2300151497Sru } 2301151497Sru n = n->next; 2302151497Sru } 2303114402Sru } 2304114402Sru} 2305114402Sru 2306151497Sruvoid environment::construct_new_line_state(node *n) 2307114402Sru{ 2308114402Sru if (is_html) { 2309151497Sru // find first glyph node which has a state. 2310151497Sru while (n != 0 && n->state == 0) 2311151497Sru n = n->next; 2312151497Sru if (n == 0 || n->state == 0) 2313151497Sru return; 2314151497Sru if (seen_space != 0) 2315151497Sru n->state->add_tag(MTSM_SP, seen_space); 2316151497Sru if (seen_eol && topdiv == curdiv) 2317151497Sru n->state->add_tag(MTSM_EOL); 2318151497Sru seen_space = 0; 2319151497Sru seen_eol = 0; 2320114402Sru } 2321114402Sru} 2322114402Sru 2323151497Sruextern int global_diverted_space; 2324151497Sru 2325151497Sruvoid environment::do_break(int do_spread) 2326114402Sru{ 2327151497Sru int was_centered = 0; 2328114402Sru if (curdiv == topdiv && topdiv->before_first_page) { 2329114402Sru topdiv->begin_page(); 2330114402Sru return; 2331114402Sru } 2332114402Sru if (current_tab) 2333114402Sru wrap_up_tab(); 2334114402Sru if (line) { 2335114402Sru // this is so that hyphenation works 2336114402Sru line = new space_node(H0, get_fill_color(), line); 2337114402Sru space_total++; 2338151497Sru possibly_break_line(0, do_spread); 2339114402Sru } 2340114402Sru while (line != 0 && line->discardable()) { 2341114402Sru width_total -= line->width(); 2342114402Sru space_total -= line->nspaces(); 2343114402Sru node *tem = line; 2344114402Sru line = line->next; 2345114402Sru delete tem; 2346114402Sru } 2347114402Sru discarding = 0; 2348114402Sru input_line_start = H0; 2349114402Sru if (line != 0) { 2350114402Sru if (fill) { 2351114402Sru switch (adjust_mode) { 2352114402Sru case ADJUST_CENTER: 2353114402Sru saved_indent += (target_text_length - width_total)/2; 2354151497Sru was_centered = 1; 2355114402Sru break; 2356114402Sru case ADJUST_RIGHT: 2357114402Sru saved_indent += target_text_length - width_total; 2358114402Sru break; 2359114402Sru } 2360114402Sru } 2361114402Sru node *tem = line; 2362114402Sru line = 0; 2363151497Sru output_line(tem, width_total, was_centered); 2364114402Sru hyphen_line_count = 0; 2365114402Sru } 2366114402Sru prev_line_interrupted = 0; 2367114402Sru#ifdef WIDOW_CONTROL 2368114402Sru mark_last_line(); 2369114402Sru output_pending_lines(); 2370114402Sru#endif /* WIDOW_CONTROL */ 2371151497Sru if (!global_diverted_space) { 2372151497Sru curdiv->modified_tag.incl(MTSM_BR); 2373151497Sru seen_break = 1; 2374151497Sru } 2375114402Sru} 2376114402Sru 2377114402Sruint environment::is_empty() 2378114402Sru{ 2379114402Sru return !current_tab && line == 0 && pending_lines == 0; 2380114402Sru} 2381114402Sru 2382114402Sruvoid do_break_request(int spread) 2383114402Sru{ 2384114402Sru while (!tok.newline() && !tok.eof()) 2385114402Sru tok.next(); 2386151497Sru if (break_flag) 2387114402Sru curenv->do_break(spread); 2388114402Sru tok.next(); 2389114402Sru} 2390114402Sru 2391114402Sruvoid break_request() 2392114402Sru{ 2393114402Sru do_break_request(0); 2394114402Sru} 2395114402Sru 2396114402Sruvoid break_spread_request() 2397114402Sru{ 2398114402Sru do_break_request(1); 2399114402Sru} 2400114402Sru 2401114402Sruvoid title() 2402114402Sru{ 2403114402Sru if (curdiv == topdiv && topdiv->before_first_page) { 2404114402Sru handle_initial_title(); 2405114402Sru return; 2406114402Sru } 2407114402Sru node *part[3]; 2408114402Sru hunits part_width[3]; 2409114402Sru part[0] = part[1] = part[2] = 0; 2410114402Sru environment env(curenv); 2411114402Sru environment *oldenv = curenv; 2412114402Sru curenv = &env; 2413114402Sru read_title_parts(part, part_width); 2414114402Sru curenv = oldenv; 2415114402Sru curenv->size = env.size; 2416114402Sru curenv->prev_size = env.prev_size; 2417114402Sru curenv->requested_size = env.requested_size; 2418114402Sru curenv->prev_requested_size = env.prev_requested_size; 2419114402Sru curenv->char_height = env.char_height; 2420114402Sru curenv->char_slant = env.char_slant; 2421114402Sru curenv->fontno = env.fontno; 2422114402Sru curenv->prev_fontno = env.prev_fontno; 2423114402Sru curenv->glyph_color = env.glyph_color; 2424114402Sru curenv->prev_glyph_color = env.prev_glyph_color; 2425114402Sru curenv->fill_color = env.fill_color; 2426114402Sru curenv->prev_fill_color = env.prev_fill_color; 2427114402Sru node *n = 0; 2428114402Sru node *p = part[2]; 2429114402Sru while (p != 0) { 2430114402Sru node *tem = p; 2431114402Sru p = p->next; 2432114402Sru tem->next = n; 2433114402Sru n = tem; 2434114402Sru } 2435151497Sru hunits length_title(curenv->title_length); 2436151497Sru hunits f = length_title - part_width[1]; 2437114402Sru hunits f2 = f/2; 2438114402Sru n = new hmotion_node(f2 - part_width[2], curenv->get_fill_color(), n); 2439114402Sru p = part[1]; 2440114402Sru while (p != 0) { 2441114402Sru node *tem = p; 2442114402Sru p = p->next; 2443114402Sru tem->next = n; 2444114402Sru n = tem; 2445114402Sru } 2446114402Sru n = new hmotion_node(f - f2 - part_width[0], curenv->get_fill_color(), n); 2447114402Sru p = part[0]; 2448114402Sru while (p != 0) { 2449114402Sru node *tem = p; 2450114402Sru p = p->next; 2451114402Sru tem->next = n; 2452114402Sru n = tem; 2453114402Sru } 2454114402Sru curenv->output_title(n, !curenv->fill, curenv->vertical_spacing, 2455151497Sru curenv->total_post_vertical_spacing(), length_title); 2456114402Sru curenv->hyphen_line_count = 0; 2457114402Sru tok.next(); 2458114402Sru} 2459114402Sru 2460114402Sruvoid adjust() 2461114402Sru{ 2462114402Sru curenv->adjust_mode |= 1; 2463114402Sru if (has_arg()) { 2464114402Sru switch (tok.ch()) { 2465114402Sru case 'l': 2466114402Sru curenv->adjust_mode = ADJUST_LEFT; 2467114402Sru break; 2468114402Sru case 'r': 2469114402Sru curenv->adjust_mode = ADJUST_RIGHT; 2470114402Sru break; 2471114402Sru case 'c': 2472114402Sru curenv->adjust_mode = ADJUST_CENTER; 2473114402Sru break; 2474114402Sru case 'b': 2475114402Sru case 'n': 2476114402Sru curenv->adjust_mode = ADJUST_BOTH; 2477114402Sru break; 2478114402Sru default: 2479114402Sru int n; 2480114402Sru if (get_integer(&n)) { 2481114402Sru if (n < 0) 2482114402Sru warning(WARN_RANGE, "negative adjustment mode"); 2483114402Sru else if (n > 5) { 2484114402Sru curenv->adjust_mode = 5; 2485114402Sru warning(WARN_RANGE, "adjustment mode `%1' out of range", n); 2486114402Sru } 2487114402Sru else 2488114402Sru curenv->adjust_mode = n; 2489114402Sru } 2490114402Sru } 2491114402Sru } 2492114402Sru skip_line(); 2493114402Sru} 2494114402Sru 2495114402Sruvoid no_adjust() 2496114402Sru{ 2497114402Sru curenv->adjust_mode &= ~1; 2498114402Sru skip_line(); 2499114402Sru} 2500114402Sru 2501114402Sruvoid do_input_trap(int continued) 2502114402Sru{ 2503114402Sru curenv->input_trap_count = 0; 2504114402Sru if (continued) 2505114402Sru curenv->continued_input_trap = 1; 2506114402Sru int n; 2507114402Sru if (has_arg() && get_integer(&n)) { 2508114402Sru if (n <= 0) 2509114402Sru warning(WARN_RANGE, 2510114402Sru "number of lines for input trap must be greater than zero"); 2511114402Sru else { 2512114402Sru symbol s = get_name(1); 2513114402Sru if (!s.is_null()) { 2514114402Sru curenv->input_trap_count = n; 2515114402Sru curenv->input_trap = s; 2516114402Sru } 2517114402Sru } 2518114402Sru } 2519114402Sru skip_line(); 2520114402Sru} 2521114402Sru 2522114402Sruvoid input_trap() 2523114402Sru{ 2524114402Sru do_input_trap(0); 2525114402Sru} 2526114402Sru 2527114402Sruvoid input_trap_continued() 2528114402Sru{ 2529114402Sru do_input_trap(1); 2530114402Sru} 2531114402Sru 2532114402Sru/* tabs */ 2533114402Sru 2534114402Sru// must not be R or C or L or a legitimate part of a number expression 2535114402Sruconst char TAB_REPEAT_CHAR = 'T'; 2536114402Sru 2537114402Srustruct tab { 2538114402Sru tab *next; 2539114402Sru hunits pos; 2540114402Sru tab_type type; 2541114402Sru tab(hunits, tab_type); 2542114402Sru enum { BLOCK = 1024 }; 2543114402Sru static tab *free_list; 2544114402Sru void *operator new(size_t); 2545114402Sru void operator delete(void *); 2546114402Sru}; 2547114402Sru 2548114402Srutab *tab::free_list = 0; 2549114402Sru 2550114402Sruvoid *tab::operator new(size_t n) 2551114402Sru{ 2552114402Sru assert(n == sizeof(tab)); 2553114402Sru if (!free_list) { 2554114402Sru free_list = (tab *)new char[sizeof(tab)*BLOCK]; 2555114402Sru for (int i = 0; i < BLOCK - 1; i++) 2556114402Sru free_list[i].next = free_list + i + 1; 2557114402Sru free_list[BLOCK-1].next = 0; 2558114402Sru } 2559114402Sru tab *p = free_list; 2560114402Sru free_list = (tab *)(free_list->next); 2561114402Sru p->next = 0; 2562114402Sru return p; 2563114402Sru} 2564114402Sru 2565114402Sru#ifdef __GNUG__ 2566114402Sru/* cfront can't cope with this. */ 2567114402Sruinline 2568114402Sru#endif 2569114402Sruvoid tab::operator delete(void *p) 2570114402Sru{ 2571114402Sru if (p) { 2572114402Sru ((tab *)p)->next = free_list; 2573114402Sru free_list = (tab *)p; 2574114402Sru } 2575114402Sru} 2576114402Sru 2577114402Srutab::tab(hunits x, tab_type t) : next(0), pos(x), type(t) 2578114402Sru{ 2579114402Sru} 2580114402Sru 2581114402Srutab_stops::tab_stops(hunits distance, tab_type type) 2582114402Sru: initial_list(0) 2583114402Sru{ 2584114402Sru repeated_list = new tab(distance, type); 2585114402Sru} 2586114402Sru 2587114402Srutab_stops::~tab_stops() 2588114402Sru{ 2589114402Sru clear(); 2590114402Sru} 2591114402Sru 2592114402Srutab_type tab_stops::distance_to_next_tab(hunits curpos, hunits *distance) 2593114402Sru{ 2594114402Sru hunits nextpos; 2595114402Sru 2596114402Sru return distance_to_next_tab(curpos, distance, &nextpos); 2597114402Sru} 2598114402Sru 2599114402Srutab_type tab_stops::distance_to_next_tab(hunits curpos, hunits *distance, 2600114402Sru hunits *nextpos) 2601114402Sru{ 2602114402Sru hunits lastpos = 0; 2603114402Sru tab *tem; 2604114402Sru for (tem = initial_list; tem && tem->pos <= curpos; tem = tem->next) 2605114402Sru lastpos = tem->pos; 2606114402Sru if (tem) { 2607114402Sru *distance = tem->pos - curpos; 2608114402Sru *nextpos = tem->pos; 2609114402Sru return tem->type; 2610114402Sru } 2611114402Sru if (repeated_list == 0) 2612114402Sru return TAB_NONE; 2613114402Sru hunits base = lastpos; 2614114402Sru for (;;) { 2615114402Sru for (tem = repeated_list; tem && tem->pos + base <= curpos; tem = tem->next) 2616114402Sru lastpos = tem->pos; 2617114402Sru if (tem) { 2618114402Sru *distance = tem->pos + base - curpos; 2619114402Sru *nextpos = tem->pos + base; 2620114402Sru return tem->type; 2621114402Sru } 2622114402Sru assert(lastpos > 0); 2623114402Sru base += lastpos; 2624114402Sru } 2625114402Sru return TAB_NONE; 2626114402Sru} 2627114402Sru 2628114402Sruconst char *tab_stops::to_string() 2629114402Sru{ 2630114402Sru static char *buf = 0; 2631114402Sru static int buf_size = 0; 2632114402Sru // figure out a maximum on the amount of space we can need 2633114402Sru int count = 0; 2634114402Sru tab *p; 2635114402Sru for (p = initial_list; p; p = p->next) 2636114402Sru ++count; 2637114402Sru for (p = repeated_list; p; p = p->next) 2638114402Sru ++count; 2639114402Sru // (10 for digits + 1 for u + 1 for 'C' or 'R') + 2 for ' &' + 1 for '\0' 2640114402Sru int need = count*12 + 3; 2641114402Sru if (buf == 0 || need > buf_size) { 2642114402Sru if (buf) 2643114402Sru a_delete buf; 2644114402Sru buf_size = need; 2645114402Sru buf = new char[buf_size]; 2646114402Sru } 2647114402Sru char *ptr = buf; 2648114402Sru for (p = initial_list; p; p = p->next) { 2649114402Sru strcpy(ptr, i_to_a(p->pos.to_units())); 2650114402Sru ptr = strchr(ptr, '\0'); 2651114402Sru *ptr++ = 'u'; 2652114402Sru *ptr = '\0'; 2653114402Sru switch (p->type) { 2654114402Sru case TAB_LEFT: 2655114402Sru break; 2656114402Sru case TAB_RIGHT: 2657114402Sru *ptr++ = 'R'; 2658114402Sru break; 2659114402Sru case TAB_CENTER: 2660114402Sru *ptr++ = 'C'; 2661114402Sru break; 2662114402Sru case TAB_NONE: 2663114402Sru default: 2664114402Sru assert(0); 2665114402Sru } 2666114402Sru } 2667114402Sru if (repeated_list) 2668114402Sru *ptr++ = TAB_REPEAT_CHAR; 2669114402Sru for (p = repeated_list; p; p = p->next) { 2670114402Sru strcpy(ptr, i_to_a(p->pos.to_units())); 2671114402Sru ptr = strchr(ptr, '\0'); 2672114402Sru *ptr++ = 'u'; 2673114402Sru *ptr = '\0'; 2674114402Sru switch (p->type) { 2675114402Sru case TAB_LEFT: 2676114402Sru break; 2677114402Sru case TAB_RIGHT: 2678114402Sru *ptr++ = 'R'; 2679114402Sru break; 2680114402Sru case TAB_CENTER: 2681114402Sru *ptr++ = 'C'; 2682114402Sru break; 2683114402Sru case TAB_NONE: 2684114402Sru default: 2685114402Sru assert(0); 2686114402Sru } 2687114402Sru } 2688114402Sru *ptr++ = '\0'; 2689114402Sru return buf; 2690114402Sru} 2691114402Sru 2692114402Srutab_stops::tab_stops() : initial_list(0), repeated_list(0) 2693114402Sru{ 2694114402Sru} 2695114402Sru 2696114402Srutab_stops::tab_stops(const tab_stops &ts) 2697114402Sru: initial_list(0), repeated_list(0) 2698114402Sru{ 2699114402Sru tab **p = &initial_list; 2700114402Sru tab *t = ts.initial_list; 2701114402Sru while (t) { 2702114402Sru *p = new tab(t->pos, t->type); 2703114402Sru t = t->next; 2704114402Sru p = &(*p)->next; 2705114402Sru } 2706114402Sru p = &repeated_list; 2707114402Sru t = ts.repeated_list; 2708114402Sru while (t) { 2709114402Sru *p = new tab(t->pos, t->type); 2710114402Sru t = t->next; 2711114402Sru p = &(*p)->next; 2712114402Sru } 2713114402Sru} 2714114402Sru 2715114402Sruvoid tab_stops::clear() 2716114402Sru{ 2717114402Sru while (initial_list) { 2718114402Sru tab *tem = initial_list; 2719114402Sru initial_list = initial_list->next; 2720114402Sru delete tem; 2721114402Sru } 2722114402Sru while (repeated_list) { 2723114402Sru tab *tem = repeated_list; 2724114402Sru repeated_list = repeated_list->next; 2725114402Sru delete tem; 2726114402Sru } 2727114402Sru} 2728114402Sru 2729114402Sruvoid tab_stops::add_tab(hunits pos, tab_type type, int repeated) 2730114402Sru{ 2731114402Sru tab **p; 2732114402Sru for (p = repeated ? &repeated_list : &initial_list; *p; p = &(*p)->next) 2733114402Sru ; 2734114402Sru *p = new tab(pos, type); 2735114402Sru} 2736114402Sru 2737114402Sru 2738114402Sruvoid tab_stops::operator=(const tab_stops &ts) 2739114402Sru{ 2740114402Sru clear(); 2741114402Sru tab **p = &initial_list; 2742114402Sru tab *t = ts.initial_list; 2743114402Sru while (t) { 2744114402Sru *p = new tab(t->pos, t->type); 2745114402Sru t = t->next; 2746114402Sru p = &(*p)->next; 2747114402Sru } 2748114402Sru p = &repeated_list; 2749114402Sru t = ts.repeated_list; 2750114402Sru while (t) { 2751114402Sru *p = new tab(t->pos, t->type); 2752114402Sru t = t->next; 2753114402Sru p = &(*p)->next; 2754114402Sru } 2755114402Sru} 2756114402Sru 2757114402Sruvoid set_tabs() 2758114402Sru{ 2759114402Sru hunits pos; 2760114402Sru hunits prev_pos = 0; 2761114402Sru int first = 1; 2762114402Sru int repeated = 0; 2763114402Sru tab_stops tabs; 2764114402Sru while (has_arg()) { 2765114402Sru if (tok.ch() == TAB_REPEAT_CHAR) { 2766114402Sru tok.next(); 2767114402Sru repeated = 1; 2768114402Sru prev_pos = 0; 2769114402Sru } 2770114402Sru if (!get_hunits(&pos, 'm', prev_pos)) 2771114402Sru break; 2772114402Sru tab_type type = TAB_LEFT; 2773114402Sru if (tok.ch() == 'C') { 2774114402Sru tok.next(); 2775114402Sru type = TAB_CENTER; 2776114402Sru } 2777114402Sru else if (tok.ch() == 'R') { 2778114402Sru tok.next(); 2779114402Sru type = TAB_RIGHT; 2780114402Sru } 2781114402Sru else if (tok.ch() == 'L') { 2782114402Sru tok.next(); 2783114402Sru } 2784114402Sru if (pos <= prev_pos && !first) 2785114402Sru warning(WARN_RANGE, 2786114402Sru "positions of tab stops must be strictly increasing"); 2787114402Sru else { 2788114402Sru tabs.add_tab(pos, type, repeated); 2789114402Sru prev_pos = pos; 2790114402Sru first = 0; 2791114402Sru } 2792114402Sru } 2793114402Sru curenv->tabs = tabs; 2794151497Sru curdiv->modified_tag.incl(MTSM_TA); 2795114402Sru skip_line(); 2796114402Sru} 2797114402Sru 2798114402Sruconst char *environment::get_tabs() 2799114402Sru{ 2800114402Sru return tabs.to_string(); 2801114402Sru} 2802114402Sru 2803114402Srutab_type environment::distance_to_next_tab(hunits *distance) 2804114402Sru{ 2805114402Sru return line_tabs 2806114402Sru ? curenv->tabs.distance_to_next_tab(get_text_length(), distance) 2807114402Sru : curenv->tabs.distance_to_next_tab(get_input_line_position(), distance); 2808114402Sru} 2809114402Sru 2810114402Srutab_type environment::distance_to_next_tab(hunits *distance, hunits *leftpos) 2811114402Sru{ 2812114402Sru return line_tabs 2813114402Sru ? curenv->tabs.distance_to_next_tab(get_text_length(), distance, leftpos) 2814114402Sru : curenv->tabs.distance_to_next_tab(get_input_line_position(), distance, 2815114402Sru leftpos); 2816114402Sru} 2817114402Sru 2818114402Sruvoid field_characters() 2819114402Sru{ 2820114402Sru field_delimiter_char = get_optional_char(); 2821114402Sru if (field_delimiter_char) 2822114402Sru padding_indicator_char = get_optional_char(); 2823114402Sru else 2824114402Sru padding_indicator_char = 0; 2825114402Sru skip_line(); 2826114402Sru} 2827114402Sru 2828114402Sruvoid line_tabs_request() 2829114402Sru{ 2830114402Sru int n; 2831114402Sru if (has_arg() && get_integer(&n)) 2832114402Sru curenv->line_tabs = n != 0; 2833114402Sru else 2834114402Sru curenv->line_tabs = 1; 2835114402Sru skip_line(); 2836114402Sru} 2837114402Sru 2838114402Sruint environment::get_line_tabs() 2839114402Sru{ 2840114402Sru return line_tabs; 2841114402Sru} 2842114402Sru 2843114402Sruvoid environment::wrap_up_tab() 2844114402Sru{ 2845114402Sru if (!current_tab) 2846114402Sru return; 2847114402Sru if (line == 0) 2848114402Sru start_line(); 2849114402Sru hunits tab_amount; 2850114402Sru switch (current_tab) { 2851114402Sru case TAB_RIGHT: 2852114402Sru tab_amount = tab_distance - tab_width; 2853114402Sru line = make_tab_node(tab_amount, line); 2854114402Sru break; 2855114402Sru case TAB_CENTER: 2856114402Sru tab_amount = tab_distance - tab_width/2; 2857114402Sru line = make_tab_node(tab_amount, line); 2858114402Sru break; 2859114402Sru case TAB_NONE: 2860114402Sru case TAB_LEFT: 2861114402Sru default: 2862114402Sru assert(0); 2863114402Sru } 2864114402Sru width_total += tab_amount; 2865114402Sru width_total += tab_width; 2866114402Sru if (current_field) { 2867114402Sru if (tab_precedes_field) { 2868114402Sru pre_field_width += tab_amount; 2869114402Sru tab_precedes_field = 0; 2870114402Sru } 2871114402Sru field_distance -= tab_amount; 2872114402Sru field_spaces += tab_field_spaces; 2873114402Sru } 2874114402Sru if (tab_contents != 0) { 2875114402Sru node *tem; 2876114402Sru for (tem = tab_contents; tem->next != 0; tem = tem->next) 2877114402Sru ; 2878114402Sru tem->next = line; 2879114402Sru line = tab_contents; 2880114402Sru } 2881114402Sru tab_field_spaces = 0; 2882114402Sru tab_contents = 0; 2883114402Sru tab_width = H0; 2884114402Sru tab_distance = H0; 2885114402Sru current_tab = TAB_NONE; 2886114402Sru} 2887114402Sru 2888114402Srunode *environment::make_tab_node(hunits d, node *next) 2889114402Sru{ 2890114402Sru if (leader_node != 0 && d < 0) { 2891114402Sru error("motion generated by leader cannot be negative"); 2892114402Sru delete leader_node; 2893114402Sru leader_node = 0; 2894114402Sru } 2895114402Sru if (!leader_node) 2896114402Sru return new hmotion_node(d, 1, 0, get_fill_color(), next); 2897114402Sru node *n = new hline_node(d, leader_node, next); 2898114402Sru leader_node = 0; 2899114402Sru return n; 2900114402Sru} 2901114402Sru 2902114402Sruvoid environment::handle_tab(int is_leader) 2903114402Sru{ 2904114402Sru hunits d; 2905151497Sru hunits absolute; 2906114402Sru if (current_tab) 2907114402Sru wrap_up_tab(); 2908114402Sru charinfo *ci = is_leader ? leader_char : tab_char; 2909114402Sru delete leader_node; 2910114402Sru leader_node = ci ? make_char_node(ci) : 0; 2911151497Sru tab_type t = distance_to_next_tab(&d, &absolute); 2912114402Sru switch (t) { 2913114402Sru case TAB_NONE: 2914114402Sru return; 2915114402Sru case TAB_LEFT: 2916151497Sru add_node(make_tag("tab L", absolute.to_units())); 2917114402Sru add_node(make_tab_node(d)); 2918114402Sru return; 2919114402Sru case TAB_RIGHT: 2920151497Sru add_node(make_tag("tab R", absolute.to_units())); 2921114402Sru break; 2922114402Sru case TAB_CENTER: 2923151497Sru add_node(make_tag("tab C", absolute.to_units())); 2924114402Sru break; 2925114402Sru default: 2926114402Sru assert(0); 2927114402Sru } 2928114402Sru tab_width = 0; 2929114402Sru tab_distance = d; 2930114402Sru tab_contents = 0; 2931114402Sru current_tab = t; 2932114402Sru tab_field_spaces = 0; 2933114402Sru} 2934114402Sru 2935114402Sruvoid environment::start_field() 2936114402Sru{ 2937114402Sru assert(!current_field); 2938114402Sru hunits d; 2939114402Sru if (distance_to_next_tab(&d) != TAB_NONE) { 2940114402Sru pre_field_width = get_text_length(); 2941114402Sru field_distance = d; 2942114402Sru current_field = 1; 2943114402Sru field_spaces = 0; 2944114402Sru tab_field_spaces = 0; 2945114402Sru for (node *p = line; p; p = p->next) 2946114402Sru if (p->nspaces()) { 2947114402Sru p->freeze_space(); 2948114402Sru space_total--; 2949114402Sru } 2950114402Sru tab_precedes_field = current_tab != TAB_NONE; 2951114402Sru } 2952114402Sru else 2953114402Sru error("zero field width"); 2954114402Sru} 2955114402Sru 2956114402Sruvoid environment::wrap_up_field() 2957114402Sru{ 2958114402Sru if (!current_tab && field_spaces == 0) 2959114402Sru add_padding(); 2960114402Sru hunits padding = field_distance - (get_text_length() - pre_field_width); 2961114402Sru if (current_tab && tab_field_spaces != 0) { 2962114402Sru hunits tab_padding = scale(padding, 2963114402Sru tab_field_spaces, 2964114402Sru field_spaces + tab_field_spaces); 2965114402Sru padding -= tab_padding; 2966114402Sru distribute_space(tab_contents, tab_field_spaces, tab_padding, 1); 2967114402Sru tab_field_spaces = 0; 2968114402Sru tab_width += tab_padding; 2969114402Sru } 2970114402Sru if (field_spaces != 0) { 2971114402Sru distribute_space(line, field_spaces, padding, 1); 2972114402Sru width_total += padding; 2973114402Sru if (current_tab) { 2974114402Sru // the start of the tab has been moved to the right by padding, so 2975114402Sru tab_distance -= padding; 2976114402Sru if (tab_distance <= H0) { 2977114402Sru // use the next tab stop instead 2978114402Sru current_tab = tabs.distance_to_next_tab(get_input_line_position() 2979114402Sru - tab_width, 2980114402Sru &tab_distance); 2981114402Sru if (current_tab == TAB_NONE || current_tab == TAB_LEFT) { 2982114402Sru width_total += tab_width; 2983114402Sru if (current_tab == TAB_LEFT) { 2984114402Sru line = make_tab_node(tab_distance, line); 2985114402Sru width_total += tab_distance; 2986114402Sru current_tab = TAB_NONE; 2987114402Sru } 2988114402Sru if (tab_contents != 0) { 2989114402Sru node *tem; 2990114402Sru for (tem = tab_contents; tem->next != 0; tem = tem->next) 2991114402Sru ; 2992114402Sru tem->next = line; 2993114402Sru line = tab_contents; 2994114402Sru tab_contents = 0; 2995114402Sru } 2996114402Sru tab_width = H0; 2997114402Sru tab_distance = H0; 2998114402Sru } 2999114402Sru } 3000114402Sru } 3001114402Sru } 3002114402Sru current_field = 0; 3003114402Sru} 3004114402Sru 3005114402Sruvoid environment::add_padding() 3006114402Sru{ 3007114402Sru if (current_tab) { 3008114402Sru tab_contents = new space_node(H0, get_fill_color(), tab_contents); 3009114402Sru tab_field_spaces++; 3010114402Sru } 3011114402Sru else { 3012114402Sru if (line == 0) 3013114402Sru start_line(); 3014114402Sru line = new space_node(H0, get_fill_color(), line); 3015114402Sru field_spaces++; 3016114402Sru } 3017114402Sru} 3018114402Sru 3019114402Srutypedef int (environment::*INT_FUNCP)(); 3020114402Srutypedef vunits (environment::*VUNITS_FUNCP)(); 3021114402Srutypedef hunits (environment::*HUNITS_FUNCP)(); 3022114402Srutypedef const char *(environment::*STRING_FUNCP)(); 3023114402Sru 3024114402Sruclass int_env_reg : public reg { 3025114402Sru INT_FUNCP func; 3026114402Sru public: 3027114402Sru int_env_reg(INT_FUNCP); 3028114402Sru const char *get_string(); 3029114402Sru int get_value(units *val); 3030114402Sru}; 3031114402Sru 3032114402Sruclass vunits_env_reg : public reg { 3033114402Sru VUNITS_FUNCP func; 3034114402Sru public: 3035114402Sru vunits_env_reg(VUNITS_FUNCP f); 3036114402Sru const char *get_string(); 3037114402Sru int get_value(units *val); 3038114402Sru}; 3039114402Sru 3040114402Sru 3041114402Sruclass hunits_env_reg : public reg { 3042114402Sru HUNITS_FUNCP func; 3043114402Sru public: 3044114402Sru hunits_env_reg(HUNITS_FUNCP f); 3045114402Sru const char *get_string(); 3046114402Sru int get_value(units *val); 3047114402Sru}; 3048114402Sru 3049114402Sruclass string_env_reg : public reg { 3050114402Sru STRING_FUNCP func; 3051114402Srupublic: 3052114402Sru string_env_reg(STRING_FUNCP); 3053114402Sru const char *get_string(); 3054114402Sru}; 3055114402Sru 3056114402Sruint_env_reg::int_env_reg(INT_FUNCP f) : func(f) 3057114402Sru{ 3058114402Sru} 3059114402Sru 3060114402Sruint int_env_reg::get_value(units *val) 3061114402Sru{ 3062114402Sru *val = (curenv->*func)(); 3063114402Sru return 1; 3064114402Sru} 3065114402Sru 3066114402Sruconst char *int_env_reg::get_string() 3067114402Sru{ 3068114402Sru return i_to_a((curenv->*func)()); 3069114402Sru} 3070114402Sru 3071114402Sruvunits_env_reg::vunits_env_reg(VUNITS_FUNCP f) : func(f) 3072114402Sru{ 3073114402Sru} 3074114402Sru 3075114402Sruint vunits_env_reg::get_value(units *val) 3076114402Sru{ 3077114402Sru *val = (curenv->*func)().to_units(); 3078114402Sru return 1; 3079114402Sru} 3080114402Sru 3081114402Sruconst char *vunits_env_reg::get_string() 3082114402Sru{ 3083114402Sru return i_to_a((curenv->*func)().to_units()); 3084114402Sru} 3085114402Sru 3086114402Sruhunits_env_reg::hunits_env_reg(HUNITS_FUNCP f) : func(f) 3087114402Sru{ 3088114402Sru} 3089114402Sru 3090114402Sruint hunits_env_reg::get_value(units *val) 3091114402Sru{ 3092114402Sru *val = (curenv->*func)().to_units(); 3093114402Sru return 1; 3094114402Sru} 3095114402Sru 3096114402Sruconst char *hunits_env_reg::get_string() 3097114402Sru{ 3098114402Sru return i_to_a((curenv->*func)().to_units()); 3099114402Sru} 3100114402Sru 3101114402Srustring_env_reg::string_env_reg(STRING_FUNCP f) : func(f) 3102114402Sru{ 3103114402Sru} 3104114402Sru 3105114402Sruconst char *string_env_reg::get_string() 3106114402Sru{ 3107114402Sru return (curenv->*func)(); 3108114402Sru} 3109114402Sru 3110114402Sruclass horizontal_place_reg : public general_reg { 3111114402Srupublic: 3112114402Sru horizontal_place_reg(); 3113114402Sru int get_value(units *); 3114114402Sru void set_value(units); 3115114402Sru}; 3116114402Sru 3117114402Sruhorizontal_place_reg::horizontal_place_reg() 3118114402Sru{ 3119114402Sru} 3120114402Sru 3121114402Sruint horizontal_place_reg::get_value(units *res) 3122114402Sru{ 3123114402Sru *res = curenv->get_input_line_position().to_units(); 3124114402Sru return 1; 3125114402Sru} 3126114402Sru 3127114402Sruvoid horizontal_place_reg::set_value(units n) 3128114402Sru{ 3129114402Sru curenv->set_input_line_position(hunits(n)); 3130114402Sru} 3131114402Sru 3132114402Sruconst char *environment::get_font_family_string() 3133114402Sru{ 3134114402Sru return family->nm.contents(); 3135114402Sru} 3136114402Sru 3137151497Sruconst char *environment::get_glyph_color_string() 3138151497Sru{ 3139151497Sru return glyph_color->nm.contents(); 3140151497Sru} 3141151497Sru 3142151497Sruconst char *environment::get_fill_color_string() 3143151497Sru{ 3144151497Sru return fill_color->nm.contents(); 3145151497Sru} 3146151497Sru 3147114402Sruconst char *environment::get_font_name_string() 3148114402Sru{ 3149114402Sru symbol f = get_font_name(fontno, this); 3150114402Sru return f.contents(); 3151114402Sru} 3152114402Sru 3153151497Sruconst char *environment::get_style_name_string() 3154151497Sru{ 3155151497Sru symbol f = get_style_name(fontno); 3156151497Sru return f.contents(); 3157151497Sru} 3158151497Sru 3159114402Sruconst char *environment::get_name_string() 3160114402Sru{ 3161114402Sru return name.contents(); 3162114402Sru} 3163114402Sru 3164114402Sru// Convert a quantity in scaled points to ascii decimal fraction. 3165114402Sru 3166114402Sruconst char *sptoa(int sp) 3167114402Sru{ 3168114402Sru assert(sp > 0); 3169114402Sru assert(sizescale > 0); 3170114402Sru if (sizescale == 1) 3171114402Sru return i_to_a(sp); 3172114402Sru if (sp % sizescale == 0) 3173114402Sru return i_to_a(sp/sizescale); 3174114402Sru // See if 1/sizescale is exactly representable as a decimal fraction, 3175114402Sru // ie its only prime factors are 2 and 5. 3176114402Sru int n = sizescale; 3177114402Sru int power2 = 0; 3178114402Sru while ((n & 1) == 0) { 3179114402Sru n >>= 1; 3180114402Sru power2++; 3181114402Sru } 3182114402Sru int power5 = 0; 3183114402Sru while ((n % 5) == 0) { 3184114402Sru n /= 5; 3185114402Sru power5++; 3186114402Sru } 3187114402Sru if (n == 1) { 3188114402Sru int decimal_point = power5 > power2 ? power5 : power2; 3189114402Sru if (decimal_point <= 10) { 3190114402Sru int factor = 1; 3191114402Sru int t; 3192114402Sru for (t = decimal_point - power2; --t >= 0;) 3193114402Sru factor *= 2; 3194114402Sru for (t = decimal_point - power5; --t >= 0;) 3195114402Sru factor *= 5; 3196114402Sru if (factor == 1 || sp <= INT_MAX/factor) 3197114402Sru return if_to_a(sp*factor, decimal_point); 3198114402Sru } 3199114402Sru } 3200114402Sru double s = double(sp)/double(sizescale); 3201114402Sru double factor = 10.0; 3202114402Sru double val = s; 3203114402Sru int decimal_point = 0; 3204114402Sru do { 3205114402Sru double v = ceil(s*factor); 3206114402Sru if (v > INT_MAX) 3207114402Sru break; 3208114402Sru val = v; 3209114402Sru factor *= 10.0; 3210114402Sru } while (++decimal_point < 10); 3211114402Sru return if_to_a(int(val), decimal_point); 3212114402Sru} 3213114402Sru 3214114402Sruconst char *environment::get_point_size_string() 3215114402Sru{ 3216114402Sru return sptoa(curenv->get_point_size()); 3217114402Sru} 3218114402Sru 3219114402Sruconst char *environment::get_requested_point_size_string() 3220114402Sru{ 3221114402Sru return sptoa(curenv->get_requested_point_size()); 3222114402Sru} 3223114402Sru 3224114402Sru#define init_int_env_reg(name, func) \ 3225114402Sru number_reg_dictionary.define(name, new int_env_reg(&environment::func)) 3226114402Sru 3227114402Sru#define init_vunits_env_reg(name, func) \ 3228114402Sru number_reg_dictionary.define(name, new vunits_env_reg(&environment::func)) 3229114402Sru 3230114402Sru#define init_hunits_env_reg(name, func) \ 3231114402Sru number_reg_dictionary.define(name, new hunits_env_reg(&environment::func)) 3232114402Sru 3233114402Sru#define init_string_env_reg(name, func) \ 3234114402Sru number_reg_dictionary.define(name, new string_env_reg(&environment::func)) 3235114402Sru 3236114402Sruvoid init_env_requests() 3237114402Sru{ 3238114402Sru init_request("ad", adjust); 3239114402Sru init_request("br", break_request); 3240114402Sru init_request("brp", break_spread_request); 3241114402Sru init_request("c2", no_break_control_char); 3242114402Sru init_request("cc", control_char); 3243114402Sru init_request("ce", center); 3244114402Sru init_request("cu", continuous_underline); 3245114402Sru init_request("ev", environment_switch); 3246114402Sru init_request("evc", environment_copy); 3247114402Sru init_request("fam", family_change); 3248114402Sru init_request("fc", field_characters); 3249114402Sru init_request("fi", fill); 3250151497Sru init_request("fcolor", fill_color_change); 3251114402Sru init_request("ft", font_change); 3252151497Sru init_request("gcolor", glyph_color_change); 3253114402Sru init_request("hc", hyphen_char); 3254114402Sru init_request("hlm", hyphen_line_max_request); 3255114402Sru init_request("hy", hyphenate_request); 3256114402Sru init_request("hym", hyphenation_margin_request); 3257114402Sru init_request("hys", hyphenation_space_request); 3258114402Sru init_request("in", indent); 3259114402Sru init_request("it", input_trap); 3260114402Sru init_request("itc", input_trap_continued); 3261114402Sru init_request("lc", leader_character); 3262114402Sru init_request("linetabs", line_tabs_request); 3263114402Sru init_request("ll", line_length); 3264114402Sru init_request("ls", line_spacing); 3265114402Sru init_request("lt", title_length); 3266114402Sru init_request("mc", margin_character); 3267114402Sru init_request("na", no_adjust); 3268114402Sru init_request("nf", no_fill); 3269114402Sru init_request("nh", no_hyphenate); 3270114402Sru init_request("nm", number_lines); 3271114402Sru init_request("nn", no_number); 3272114402Sru init_request("ps", point_size); 3273114402Sru init_request("pvs", post_vertical_spacing); 3274114402Sru init_request("rj", right_justify); 3275114402Sru init_request("sizes", override_sizes); 3276114402Sru init_request("ss", space_size); 3277114402Sru init_request("ta", set_tabs); 3278114402Sru init_request("ti", temporary_indent); 3279114402Sru init_request("tc", tab_character); 3280114402Sru init_request("tl", title); 3281114402Sru init_request("ul", underline); 3282114402Sru init_request("vs", vertical_spacing); 3283114402Sru#ifdef WIDOW_CONTROL 3284114402Sru init_request("wdc", widow_control_request); 3285114402Sru#endif /* WIDOW_CONTROL */ 3286114402Sru init_int_env_reg(".b", get_bold); 3287114402Sru init_vunits_env_reg(".cdp", get_prev_char_depth); 3288114402Sru init_int_env_reg(".ce", get_center_lines); 3289114402Sru init_vunits_env_reg(".cht", get_prev_char_height); 3290114402Sru init_hunits_env_reg(".csk", get_prev_char_skew); 3291114402Sru init_string_env_reg(".ev", get_name_string); 3292114402Sru init_int_env_reg(".f", get_font); 3293114402Sru init_string_env_reg(".fam", get_font_family_string); 3294114402Sru init_string_env_reg(".fn", get_font_name_string); 3295114402Sru init_int_env_reg(".height", get_char_height); 3296114402Sru init_int_env_reg(".hlc", get_hyphen_line_count); 3297114402Sru init_int_env_reg(".hlm", get_hyphen_line_max); 3298114402Sru init_int_env_reg(".hy", get_hyphenation_flags); 3299114402Sru init_hunits_env_reg(".hym", get_hyphenation_margin); 3300114402Sru init_hunits_env_reg(".hys", get_hyphenation_space); 3301114402Sru init_hunits_env_reg(".i", get_indent); 3302114402Sru init_hunits_env_reg(".in", get_saved_indent); 3303114402Sru init_int_env_reg(".int", get_prev_line_interrupted); 3304114402Sru init_int_env_reg(".linetabs", get_line_tabs); 3305114402Sru init_hunits_env_reg(".lt", get_title_length); 3306114402Sru init_int_env_reg(".j", get_adjust_mode); 3307114402Sru init_hunits_env_reg(".k", get_text_length); 3308114402Sru init_int_env_reg(".L", get_line_spacing); 3309114402Sru init_hunits_env_reg(".l", get_line_length); 3310114402Sru init_hunits_env_reg(".ll", get_saved_line_length); 3311151497Sru init_string_env_reg(".M", get_fill_color_string); 3312151497Sru init_string_env_reg(".m", get_glyph_color_string); 3313114402Sru init_hunits_env_reg(".n", get_prev_text_length); 3314114402Sru init_int_env_reg(".ps", get_point_size); 3315114402Sru init_int_env_reg(".psr", get_requested_point_size); 3316114402Sru init_vunits_env_reg(".pvs", get_post_vertical_spacing); 3317114402Sru init_int_env_reg(".rj", get_right_justify_lines); 3318114402Sru init_string_env_reg(".s", get_point_size_string); 3319114402Sru init_int_env_reg(".slant", get_char_slant); 3320114402Sru init_int_env_reg(".ss", get_space_size); 3321114402Sru init_int_env_reg(".sss", get_sentence_space_size); 3322114402Sru init_string_env_reg(".sr", get_requested_point_size_string); 3323151497Sru init_string_env_reg(".sty", get_style_name_string); 3324114402Sru init_string_env_reg(".tabs", get_tabs); 3325114402Sru init_int_env_reg(".u", get_fill); 3326114402Sru init_vunits_env_reg(".v", get_vertical_spacing); 3327114402Sru init_hunits_env_reg(".w", get_prev_char_width); 3328114402Sru number_reg_dictionary.define("ct", new variable_reg(&ct_reg_contents)); 3329114402Sru number_reg_dictionary.define("hp", new horizontal_place_reg); 3330114402Sru number_reg_dictionary.define("ln", new variable_reg(&next_line_number)); 3331114402Sru number_reg_dictionary.define("rsb", new variable_reg(&rsb_reg_contents)); 3332114402Sru number_reg_dictionary.define("rst", new variable_reg(&rst_reg_contents)); 3333114402Sru number_reg_dictionary.define("sb", new variable_reg(&sb_reg_contents)); 3334114402Sru number_reg_dictionary.define("skw", new variable_reg(&skw_reg_contents)); 3335114402Sru number_reg_dictionary.define("ssc", new variable_reg(&ssc_reg_contents)); 3336114402Sru number_reg_dictionary.define("st", new variable_reg(&st_reg_contents)); 3337114402Sru} 3338114402Sru 3339114402Sru// Hyphenation - TeX's hyphenation algorithm with a less fancy implementation. 3340114402Sru 3341114402Srustruct trie_node; 3342114402Sru 3343114402Sruclass trie { 3344114402Sru trie_node *tp; 3345114402Sru virtual void do_match(int len, void *val) = 0; 3346114402Sru virtual void do_delete(void *) = 0; 3347114402Sru void delete_trie_node(trie_node *); 3348114402Srupublic: 3349114402Sru trie() : tp(0) {} 3350114402Sru virtual ~trie(); // virtual to shut up g++ 3351114402Sru void insert(const char *, int, void *); 3352114402Sru // find calls do_match for each match it finds 3353114402Sru void find(const char *pat, int patlen); 3354114402Sru void clear(); 3355114402Sru}; 3356114402Sru 3357114402Sruclass hyphen_trie : private trie { 3358114402Sru int *h; 3359114402Sru void do_match(int i, void *v); 3360114402Sru void do_delete(void *v); 3361114402Sru void insert_pattern(const char *pat, int patlen, int *num); 3362114402Sru void insert_hyphenation(dictionary *ex, const char *pat, int patlen); 3363114402Sru int hpf_getc(FILE *f); 3364114402Srupublic: 3365114402Sru hyphen_trie() {} 3366114402Sru ~hyphen_trie() {} 3367114402Sru void hyphenate(const char *word, int len, int *hyphens); 3368114402Sru void read_patterns_file(const char *name, int append, dictionary *ex); 3369114402Sru}; 3370114402Sru 3371114402Srustruct hyphenation_language { 3372114402Sru symbol name; 3373114402Sru dictionary exceptions; 3374114402Sru hyphen_trie patterns; 3375114402Sru hyphenation_language(symbol nm) : name(nm), exceptions(501) {} 3376114402Sru ~hyphenation_language() { } 3377114402Sru}; 3378114402Sru 3379114402Srudictionary language_dictionary(5); 3380114402Sruhyphenation_language *current_language = 0; 3381114402Sru 3382114402Srustatic void set_hyphenation_language() 3383114402Sru{ 3384114402Sru symbol nm = get_name(1); 3385114402Sru if (!nm.is_null()) { 3386114402Sru current_language = (hyphenation_language *)language_dictionary.lookup(nm); 3387114402Sru if (!current_language) { 3388114402Sru current_language = new hyphenation_language(nm); 3389114402Sru (void)language_dictionary.lookup(nm, (void *)current_language); 3390114402Sru } 3391114402Sru } 3392114402Sru skip_line(); 3393114402Sru} 3394114402Sru 3395114402Sruconst int WORD_MAX = 256; // we use unsigned char for offsets in 3396114402Sru // hyphenation exceptions 3397114402Sru 3398114402Srustatic void hyphen_word() 3399114402Sru{ 3400114402Sru if (!current_language) { 3401114402Sru error("no current hyphenation language"); 3402114402Sru skip_line(); 3403114402Sru return; 3404114402Sru } 3405114402Sru char buf[WORD_MAX + 1]; 3406114402Sru unsigned char pos[WORD_MAX + 2]; 3407114402Sru for (;;) { 3408114402Sru tok.skip(); 3409114402Sru if (tok.newline() || tok.eof()) 3410114402Sru break; 3411114402Sru int i = 0; 3412114402Sru int npos = 0; 3413114402Sru while (i < WORD_MAX && !tok.space() && !tok.newline() && !tok.eof()) { 3414114402Sru charinfo *ci = tok.get_char(1); 3415114402Sru if (ci == 0) { 3416114402Sru skip_line(); 3417114402Sru return; 3418114402Sru } 3419114402Sru tok.next(); 3420114402Sru if (ci->get_ascii_code() == '-') { 3421114402Sru if (i > 0 && (npos == 0 || pos[npos - 1] != i)) 3422114402Sru pos[npos++] = i; 3423114402Sru } 3424114402Sru else { 3425151497Sru unsigned char c = ci->get_hyphenation_code(); 3426114402Sru if (c == 0) 3427114402Sru break; 3428114402Sru buf[i++] = c; 3429114402Sru } 3430114402Sru } 3431114402Sru if (i > 0) { 3432114402Sru pos[npos] = 0; 3433114402Sru buf[i] = 0; 3434114402Sru unsigned char *tem = new unsigned char[npos + 1]; 3435114402Sru memcpy(tem, pos, npos + 1); 3436114402Sru tem = (unsigned char *)current_language->exceptions.lookup(symbol(buf), 3437114402Sru tem); 3438114402Sru if (tem) 3439114402Sru a_delete tem; 3440114402Sru } 3441114402Sru } 3442114402Sru skip_line(); 3443114402Sru} 3444114402Sru 3445114402Srustruct trie_node { 3446114402Sru char c; 3447114402Sru trie_node *down; 3448114402Sru trie_node *right; 3449114402Sru void *val; 3450114402Sru trie_node(char, trie_node *); 3451114402Sru}; 3452114402Sru 3453114402Srutrie_node::trie_node(char ch, trie_node *p) 3454114402Sru: c(ch), down(0), right(p), val(0) 3455114402Sru{ 3456114402Sru} 3457114402Sru 3458114402Srutrie::~trie() 3459114402Sru{ 3460114402Sru clear(); 3461114402Sru} 3462114402Sru 3463114402Sruvoid trie::clear() 3464114402Sru{ 3465114402Sru delete_trie_node(tp); 3466114402Sru tp = 0; 3467114402Sru} 3468114402Sru 3469114402Sru 3470114402Sruvoid trie::delete_trie_node(trie_node *p) 3471114402Sru{ 3472114402Sru if (p) { 3473114402Sru delete_trie_node(p->down); 3474114402Sru delete_trie_node(p->right); 3475114402Sru if (p->val) 3476114402Sru do_delete(p->val); 3477114402Sru delete p; 3478114402Sru } 3479114402Sru} 3480114402Sru 3481114402Sruvoid trie::insert(const char *pat, int patlen, void *val) 3482114402Sru{ 3483114402Sru trie_node **p = &tp; 3484114402Sru assert(patlen > 0 && pat != 0); 3485114402Sru for (;;) { 3486114402Sru while (*p != 0 && (*p)->c < pat[0]) 3487114402Sru p = &((*p)->right); 3488114402Sru if (*p == 0 || (*p)->c != pat[0]) 3489114402Sru *p = new trie_node(pat[0], *p); 3490114402Sru if (--patlen == 0) { 3491114402Sru (*p)->val = val; 3492114402Sru break; 3493114402Sru } 3494114402Sru ++pat; 3495114402Sru p = &((*p)->down); 3496114402Sru } 3497114402Sru} 3498114402Sru 3499114402Sruvoid trie::find(const char *pat, int patlen) 3500114402Sru{ 3501114402Sru trie_node *p = tp; 3502114402Sru for (int i = 0; p != 0 && i < patlen; i++) { 3503114402Sru while (p != 0 && p->c < pat[i]) 3504114402Sru p = p->right; 3505114402Sru if (p != 0 && p->c == pat[i]) { 3506114402Sru if (p->val != 0) 3507114402Sru do_match(i+1, p->val); 3508114402Sru p = p->down; 3509114402Sru } 3510114402Sru else 3511114402Sru break; 3512114402Sru } 3513114402Sru} 3514114402Sru 3515114402Srustruct operation { 3516114402Sru operation *next; 3517114402Sru short distance; 3518114402Sru short num; 3519114402Sru operation(int, int, operation *); 3520114402Sru}; 3521114402Sru 3522114402Sruoperation::operation(int i, int j, operation *op) 3523114402Sru: next(op), distance(j), num(i) 3524114402Sru{ 3525114402Sru} 3526114402Sru 3527114402Sruvoid hyphen_trie::insert_pattern(const char *pat, int patlen, int *num) 3528114402Sru{ 3529114402Sru operation *op = 0; 3530114402Sru for (int i = 0; i < patlen+1; i++) 3531114402Sru if (num[i] != 0) 3532114402Sru op = new operation(num[i], patlen - i, op); 3533114402Sru insert(pat, patlen, op); 3534114402Sru} 3535114402Sru 3536114402Sruvoid hyphen_trie::insert_hyphenation(dictionary *ex, const char *pat, 3537114402Sru int patlen) 3538114402Sru{ 3539114402Sru char buf[WORD_MAX + 1]; 3540114402Sru unsigned char pos[WORD_MAX + 2]; 3541114402Sru int i = 0, j = 0; 3542114402Sru int npos = 0; 3543114402Sru while (j < patlen) { 3544114402Sru unsigned char c = pat[j++]; 3545114402Sru if (c == '-') { 3546114402Sru if (i > 0 && (npos == 0 || pos[npos - 1] != i)) 3547114402Sru pos[npos++] = i; 3548114402Sru } 3549114402Sru else 3550114402Sru buf[i++] = hpf_code_table[c]; 3551114402Sru } 3552114402Sru if (i > 0) { 3553114402Sru pos[npos] = 0; 3554114402Sru buf[i] = 0; 3555114402Sru unsigned char *tem = new unsigned char[npos + 1]; 3556114402Sru memcpy(tem, pos, npos + 1); 3557114402Sru tem = (unsigned char *)ex->lookup(symbol(buf), tem); 3558114402Sru if (tem) 3559114402Sru a_delete tem; 3560114402Sru } 3561114402Sru} 3562114402Sru 3563114402Sruvoid hyphen_trie::hyphenate(const char *word, int len, int *hyphens) 3564114402Sru{ 3565114402Sru int j; 3566114402Sru for (j = 0; j < len + 1; j++) 3567114402Sru hyphens[j] = 0; 3568114402Sru for (j = 0; j < len - 1; j++) { 3569114402Sru h = hyphens + j; 3570114402Sru find(word + j, len - j); 3571114402Sru } 3572114402Sru} 3573114402Sru 3574114402Sruinline int max(int m, int n) 3575114402Sru{ 3576114402Sru return m > n ? m : n; 3577114402Sru} 3578114402Sru 3579114402Sruvoid hyphen_trie::do_match(int i, void *v) 3580114402Sru{ 3581114402Sru operation *op = (operation *)v; 3582114402Sru while (op != 0) { 3583114402Sru h[i - op->distance] = max(h[i - op->distance], op->num); 3584114402Sru op = op->next; 3585114402Sru } 3586114402Sru} 3587114402Sru 3588114402Sruvoid hyphen_trie::do_delete(void *v) 3589114402Sru{ 3590114402Sru operation *op = (operation *)v; 3591114402Sru while (op) { 3592114402Sru operation *tem = op; 3593114402Sru op = tem->next; 3594114402Sru delete tem; 3595114402Sru } 3596114402Sru} 3597114402Sru 3598114402Sru/* We use very simple rules to parse TeX's hyphenation patterns. 3599114402Sru 3600114402Sru . `%' starts a comment even if preceded by `\'. 3601114402Sru 3602114402Sru . No support for digraphs and like `\$'. 3603114402Sru 3604114402Sru . `^^xx' (`x' is 0-9 or a-f), and `^^x' (character code of `x' in the 3605114402Sru range 0-127) are recognized; other use of `^' causes an error. 3606114402Sru 3607114402Sru . No macro expansion. 3608114402Sru 3609114402Sru . We check for the expression `\patterns{...}' (possibly with 3610114402Sru whitespace before and after the braces). Everything between the 3611114402Sru braces is taken as hyphenation patterns. Consequently, `{' and `}' 3612114402Sru are not allowed in patterns. 3613114402Sru 3614114402Sru . Similarly, `\hyphenation{...}' gives a list of hyphenation 3615114402Sru exceptions. 3616114402Sru 3617114402Sru . `\endinput' is recognized also. 3618114402Sru 3619114402Sru . For backwards compatibility, if `\patterns' is missing, the 3620114402Sru whole file is treated as a list of hyphenation patterns (only 3621114402Sru recognizing `%' as the start of a comment. 3622114402Sru 3623114402Sru*/ 3624114402Sru 3625114402Sruint hyphen_trie::hpf_getc(FILE *f) 3626114402Sru{ 3627114402Sru int c = getc(f); 3628114402Sru int c1; 3629114402Sru int cc = 0; 3630114402Sru if (c != '^') 3631114402Sru return c; 3632114402Sru c = getc(f); 3633114402Sru if (c != '^') 3634114402Sru goto fail; 3635114402Sru c = getc(f); 3636114402Sru c1 = getc(f); 3637114402Sru if (((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')) 3638114402Sru && ((c1 >= '0' && c1 <= '9') || (c1 >= 'a' && c1 <= 'f'))) { 3639114402Sru if (c >= '0' && c <= '9') 3640114402Sru c -= '0'; 3641114402Sru else 3642114402Sru c = c - 'a' + 10; 3643114402Sru if (c1 >= '0' && c1 <= '9') 3644114402Sru c1 -= '0'; 3645114402Sru else 3646114402Sru c1 = c1 - 'a' + 10; 3647114402Sru cc = c * 16 + c1; 3648114402Sru } 3649114402Sru else { 3650114402Sru ungetc(c1, f); 3651114402Sru if (c >= 0 && c <= 63) 3652114402Sru cc = c + 64; 3653114402Sru else if (c >= 64 && c <= 127) 3654114402Sru cc = c - 64; 3655114402Sru else 3656114402Sru goto fail; 3657114402Sru } 3658114402Sru return cc; 3659114402Srufail: 3660114402Sru error("invalid ^, ^^x, or ^^xx character in hyphenation patterns file"); 3661114402Sru return c; 3662114402Sru} 3663114402Sru 3664114402Sruvoid hyphen_trie::read_patterns_file(const char *name, int append, 3665114402Sru dictionary *ex) 3666114402Sru{ 3667114402Sru if (!append) 3668114402Sru clear(); 3669114402Sru char buf[WORD_MAX]; 3670114402Sru for (int i = 0; i < WORD_MAX; i++) 3671114402Sru buf[i] = 0; 3672114402Sru int num[WORD_MAX+1]; 3673114402Sru errno = 0; 3674114402Sru char *path = 0; 3675114402Sru FILE *fp = mac_path->open_file(name, &path); 3676114402Sru if (fp == 0) { 3677114402Sru error("can't find hyphenation patterns file `%1'", name); 3678114402Sru return; 3679114402Sru } 3680114402Sru int c = hpf_getc(fp); 3681114402Sru int have_patterns = 0; // we've seen \patterns 3682114402Sru int final_pattern = 0; // 1 if we have a trailing closing brace 3683114402Sru int have_hyphenation = 0; // we've seen \hyphenation 3684114402Sru int final_hyphenation = 0; // 1 if we have a trailing closing brace 3685114402Sru int have_keyword = 0; // we've seen either \patterns or \hyphenation 3686114402Sru int traditional = 0; // don't handle \patterns 3687114402Sru for (;;) { 3688114402Sru for (;;) { 3689114402Sru if (c == '%') { // skip comments 3690114402Sru do { 3691114402Sru c = getc(fp); 3692114402Sru } while (c != EOF && c != '\n'); 3693114402Sru } 3694114402Sru if (c == EOF || !csspace(c)) 3695114402Sru break; 3696114402Sru c = hpf_getc(fp); 3697114402Sru } 3698114402Sru if (c == EOF) { 3699114402Sru if (have_keyword || traditional) // we are done 3700114402Sru break; 3701114402Sru else { // rescan file in `traditional' mode 3702114402Sru rewind(fp); 3703114402Sru traditional = 1; 3704114402Sru c = hpf_getc(fp); 3705114402Sru continue; 3706114402Sru } 3707114402Sru } 3708114402Sru int i = 0; 3709114402Sru num[0] = 0; 3710114402Sru if (!(c == '{' || c == '}')) { // skip braces at line start 3711114402Sru do { // scan patterns 3712114402Sru if (csdigit(c)) 3713114402Sru num[i] = c - '0'; 3714114402Sru else { 3715114402Sru buf[i++] = c; 3716114402Sru num[i] = 0; 3717114402Sru } 3718114402Sru c = hpf_getc(fp); 3719114402Sru } while (i < WORD_MAX && c != EOF && !csspace(c) 3720114402Sru && c != '%' && c != '{' && c != '}'); 3721114402Sru } 3722114402Sru if (!traditional) { 3723114402Sru if (i >= 9 && !strncmp(buf + i - 9, "\\patterns", 9)) { 3724114402Sru while (csspace(c)) 3725114402Sru c = hpf_getc(fp); 3726114402Sru if (c == '{') { 3727114402Sru if (have_patterns || have_hyphenation) 3728151497Sru error("\\patterns not allowed inside of %1 group", 3729151497Sru have_patterns ? "\\patterns" : "\\hyphenation"); 3730114402Sru else { 3731114402Sru have_patterns = 1; 3732114402Sru have_keyword = 1; 3733114402Sru } 3734114402Sru c = hpf_getc(fp); 3735114402Sru continue; 3736114402Sru } 3737114402Sru } 3738114402Sru else if (i >= 12 && !strncmp(buf + i - 12, "\\hyphenation", 12)) { 3739114402Sru while (csspace(c)) 3740114402Sru c = hpf_getc(fp); 3741114402Sru if (c == '{') { 3742114402Sru if (have_patterns || have_hyphenation) 3743151497Sru error("\\hyphenation not allowed inside of %1 group", 3744151497Sru have_patterns ? "\\patterns" : "\\hyphenation"); 3745114402Sru else { 3746114402Sru have_hyphenation = 1; 3747114402Sru have_keyword = 1; 3748114402Sru } 3749114402Sru c = hpf_getc(fp); 3750114402Sru continue; 3751114402Sru } 3752114402Sru } 3753114402Sru else if (strstr(buf, "\\endinput")) { 3754114402Sru if (have_patterns || have_hyphenation) 3755114402Sru error("found \\endinput inside of %1 group", 3756114402Sru have_patterns ? "\\patterns" : "\\hyphenation"); 3757114402Sru break; 3758114402Sru } 3759114402Sru else if (c == '}') { 3760114402Sru if (have_patterns) { 3761114402Sru have_patterns = 0; 3762114402Sru if (i > 0) 3763114402Sru final_pattern = 1; 3764114402Sru } 3765114402Sru else if (have_hyphenation) { 3766114402Sru have_hyphenation = 0; 3767114402Sru if (i > 0) 3768114402Sru final_hyphenation = 1; 3769114402Sru } 3770114402Sru c = hpf_getc(fp); 3771114402Sru } 3772151497Sru else if (c == '{') { 3773151497Sru if (have_patterns || have_hyphenation) 3774151497Sru error("`{' not allowed within %1 group", 3775151497Sru have_patterns ? "\\patterns" : "\\hyphenation"); 3776151497Sru c = hpf_getc(fp); // skipped if not starting \patterns 3777151497Sru // or \hyphenation 3778151497Sru } 3779114402Sru } 3780151497Sru else { 3781151497Sru if (c == '{' || c == '}') 3782151497Sru c = hpf_getc(fp); 3783151497Sru } 3784114402Sru if (i > 0) { 3785114402Sru if (have_patterns || final_pattern || traditional) { 3786114402Sru for (int j = 0; j < i; j++) 3787114402Sru buf[j] = hpf_code_table[(unsigned char)buf[j]]; 3788114402Sru insert_pattern(buf, i, num); 3789114402Sru final_pattern = 0; 3790114402Sru } 3791114402Sru else if (have_hyphenation || final_hyphenation) { 3792114402Sru insert_hyphenation(ex, buf, i); 3793114402Sru final_hyphenation = 0; 3794114402Sru } 3795114402Sru } 3796114402Sru } 3797114402Sru fclose(fp); 3798114402Sru a_delete path; 3799114402Sru return; 3800114402Sru} 3801114402Sru 3802114402Sruvoid hyphenate(hyphen_list *h, unsigned flags) 3803114402Sru{ 3804114402Sru if (!current_language) 3805114402Sru return; 3806114402Sru while (h) { 3807114402Sru while (h && h->hyphenation_code == 0) 3808114402Sru h = h->next; 3809114402Sru int len = 0; 3810114402Sru char hbuf[WORD_MAX+2]; 3811114402Sru char *buf = hbuf + 1; 3812114402Sru hyphen_list *tem; 3813114402Sru for (tem = h; tem && len < WORD_MAX; tem = tem->next) { 3814114402Sru if (tem->hyphenation_code != 0) 3815114402Sru buf[len++] = tem->hyphenation_code; 3816114402Sru else 3817114402Sru break; 3818114402Sru } 3819114402Sru hyphen_list *nexth = tem; 3820114402Sru if (len > 2) { 3821114402Sru buf[len] = 0; 3822114402Sru unsigned char *pos 3823114402Sru = (unsigned char *)current_language->exceptions.lookup(buf); 3824114402Sru if (pos != 0) { 3825114402Sru int j = 0; 3826114402Sru int i = 1; 3827114402Sru for (tem = h; tem != 0; tem = tem->next, i++) 3828114402Sru if (pos[j] == i) { 3829114402Sru tem->hyphen = 1; 3830114402Sru j++; 3831114402Sru } 3832114402Sru } 3833114402Sru else { 3834114402Sru hbuf[0] = hbuf[len+1] = '.'; 3835114402Sru int num[WORD_MAX+3]; 3836114402Sru current_language->patterns.hyphenate(hbuf, len+2, num); 3837114402Sru int i; 3838114402Sru num[2] = 0; 3839114402Sru if (flags & 8) 3840114402Sru num[3] = 0; 3841114402Sru if (flags & 4) 3842114402Sru --len; 3843114402Sru for (i = 2, tem = h; i < len && tem; tem = tem->next, i++) 3844114402Sru if (num[i] & 1) 3845114402Sru tem->hyphen = 1; 3846114402Sru } 3847114402Sru } 3848114402Sru h = nexth; 3849114402Sru } 3850114402Sru} 3851114402Sru 3852114402Srustatic void do_hyphenation_patterns_file(int append) 3853114402Sru{ 3854114402Sru symbol name = get_long_name(1); 3855114402Sru if (!name.is_null()) { 3856114402Sru if (!current_language) 3857114402Sru error("no current hyphenation language"); 3858114402Sru else 3859114402Sru current_language->patterns.read_patterns_file( 3860114402Sru name.contents(), append, 3861114402Sru ¤t_language->exceptions); 3862114402Sru } 3863114402Sru skip_line(); 3864114402Sru} 3865114402Sru 3866114402Srustatic void hyphenation_patterns_file() 3867114402Sru{ 3868114402Sru do_hyphenation_patterns_file(0); 3869114402Sru} 3870114402Sru 3871114402Srustatic void hyphenation_patterns_file_append() 3872114402Sru{ 3873114402Sru do_hyphenation_patterns_file(1); 3874114402Sru} 3875114402Sru 3876114402Sruclass hyphenation_language_reg : public reg { 3877114402Srupublic: 3878114402Sru const char *get_string(); 3879114402Sru}; 3880114402Sru 3881114402Sruconst char *hyphenation_language_reg::get_string() 3882114402Sru{ 3883114402Sru return current_language ? current_language->name.contents() : ""; 3884114402Sru} 3885114402Sru 3886114402Sruvoid init_hyphen_requests() 3887114402Sru{ 3888114402Sru init_request("hw", hyphen_word); 3889114402Sru init_request("hla", set_hyphenation_language); 3890114402Sru init_request("hpf", hyphenation_patterns_file); 3891114402Sru init_request("hpfa", hyphenation_patterns_file_append); 3892114402Sru number_reg_dictionary.define(".hla", new hyphenation_language_reg); 3893114402Sru} 3894