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