1// -*- C++ -*- 2/* Copyright (C) 2003, 2004 Free Software Foundation, Inc. 3 Written by Gaius Mulley (gaius@glam.ac.uk) 4 5This file is part of groff. 6 7groff is free software; you can redistribute it and/or modify it under 8the terms of the GNU General Public License as published by the Free 9Software Foundation; either version 2, or (at your option) any later 10version. 11 12groff is distributed in the hope that it will be useful, but WITHOUT ANY 13WARRANTY; without even the implied warranty of MERCHANTABILITY or 14FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15for more details. 16 17You should have received a copy of the GNU General Public License along 18with groff; see the file COPYING. If not, write to the Free Software 19Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */ 20 21#define DEBUGGING 22 23extern int debug_state; 24 25#include "troff.h" 26#include "hvunits.h" 27#include "stringclass.h" 28#include "mtsm.h" 29#include "env.h" 30 31static int no_of_statems = 0; // debugging aid 32 33int_value::int_value() 34: value(0), is_known(0) 35{ 36} 37 38int_value::~int_value() 39{ 40} 41 42void int_value::diff(FILE *fp, const char *s, int_value compare) 43{ 44 if (differs(compare)) { 45 fputs("x X ", fp); 46 fputs(s, fp); 47 fputs(" ", fp); 48 fputs(i_to_a(compare.value), fp); 49 fputs("\n", fp); 50 value = compare.value; 51 is_known = 1; 52 if (debug_state) 53 fflush(fp); 54 } 55} 56 57void int_value::set(int v) 58{ 59 is_known = 1; 60 value = v; 61} 62 63void int_value::unset() 64{ 65 is_known = 0; 66} 67 68void int_value::set_if_unknown(int v) 69{ 70 if (!is_known) 71 set(v); 72} 73 74int int_value::differs(int_value compare) 75{ 76 return compare.is_known 77 && (!is_known || value != compare.value); 78} 79 80bool_value::bool_value() 81{ 82} 83 84bool_value::~bool_value() 85{ 86} 87 88void bool_value::diff(FILE *fp, const char *s, bool_value compare) 89{ 90 if (differs(compare)) { 91 fputs("x X ", fp); 92 fputs(s, fp); 93 fputs("\n", fp); 94 value = compare.value; 95 is_known = 1; 96 if (debug_state) 97 fflush(fp); 98 } 99} 100 101units_value::units_value() 102{ 103} 104 105units_value::~units_value() 106{ 107} 108 109void units_value::diff(FILE *fp, const char *s, units_value compare) 110{ 111 if (differs(compare)) { 112 fputs("x X ", fp); 113 fputs(s, fp); 114 fputs(" ", fp); 115 fputs(i_to_a(compare.value), fp); 116 fputs("\n", fp); 117 value = compare.value; 118 is_known = 1; 119 if (debug_state) 120 fflush(fp); 121 } 122} 123 124void units_value::set(hunits v) 125{ 126 is_known = 1; 127 value = v.to_units(); 128} 129 130int units_value::differs(units_value compare) 131{ 132 return compare.is_known 133 && (!is_known || value != compare.value); 134} 135 136string_value::string_value() 137: value(string("")), is_known(0) 138{ 139} 140 141string_value::~string_value() 142{ 143} 144 145void string_value::diff(FILE *fp, const char *s, string_value compare) 146{ 147 if (differs(compare)) { 148 fputs("x X ", fp); 149 fputs(s, fp); 150 fputs(" ", fp); 151 fputs(compare.value.contents(), fp); 152 fputs("\n", fp); 153 value = compare.value; 154 is_known = 1; 155 } 156} 157 158void string_value::set(string v) 159{ 160 is_known = 1; 161 value = v; 162} 163 164void string_value::unset() 165{ 166 is_known = 0; 167} 168 169int string_value::differs(string_value compare) 170{ 171 return compare.is_known 172 && (!is_known || value != compare.value); 173} 174 175statem::statem() 176{ 177 issue_no = no_of_statems; 178 no_of_statems++; 179} 180 181statem::statem(statem *copy) 182{ 183 int i; 184 for (i = 0; i < LAST_BOOL; i++) 185 bool_values[i] = copy->bool_values[i]; 186 for (i = 0; i < LAST_INT; i++) 187 int_values[i] = copy->int_values[i]; 188 for (i = 0; i < LAST_UNITS; i++) 189 units_values[i] = copy->units_values[i]; 190 for (i = 0; i < LAST_STRING; i++) 191 string_values[i] = copy->string_values[i]; 192 issue_no = copy->issue_no; 193} 194 195statem::~statem() 196{ 197} 198 199void statem::flush(FILE *fp, statem *compare) 200{ 201 int_values[MTSM_FI].diff(fp, "devtag:.fi", 202 compare->int_values[MTSM_FI]); 203 int_values[MTSM_RJ].diff(fp, "devtag:.rj", 204 compare->int_values[MTSM_RJ]); 205 int_values[MTSM_SP].diff(fp, "devtag:.sp", 206 compare->int_values[MTSM_SP]); 207 units_values[MTSM_IN].diff(fp, "devtag:.in", 208 compare->units_values[MTSM_IN]); 209 units_values[MTSM_LL].diff(fp, "devtag:.ll", 210 compare->units_values[MTSM_LL]); 211 units_values[MTSM_PO].diff(fp, "devtag:.po", 212 compare->units_values[MTSM_PO]); 213 string_values[MTSM_TA].diff(fp, "devtag:.ta", 214 compare->string_values[MTSM_TA]); 215 units_values[MTSM_TI].diff(fp, "devtag:.ti", 216 compare->units_values[MTSM_TI]); 217 int_values[MTSM_CE].diff(fp, "devtag:.ce", 218 compare->int_values[MTSM_CE]); 219 bool_values[MTSM_EOL].diff(fp, "devtag:.eol", 220 compare->bool_values[MTSM_EOL]); 221 bool_values[MTSM_BR].diff(fp, "devtag:.br", 222 compare->bool_values[MTSM_BR]); 223 if (debug_state) { 224 fprintf(stderr, "compared state %d\n", compare->issue_no); 225 fflush(stderr); 226 } 227} 228 229void statem::add_tag(int_value_state t, int v) 230{ 231 int_values[t].set(v); 232} 233 234void statem::add_tag(units_value_state t, hunits v) 235{ 236 units_values[t].set(v); 237} 238 239void statem::add_tag(bool_value_state t) 240{ 241 bool_values[t].set(1); 242} 243 244void statem::add_tag(string_value_state t, string v) 245{ 246 string_values[t].set(v); 247} 248 249void statem::add_tag_if_unknown(int_value_state t, int v) 250{ 251 int_values[t].set_if_unknown(v); 252} 253 254void statem::sub_tag_ce() 255{ 256 int_values[MTSM_CE].unset(); 257} 258 259/* 260 * add_tag_ta - add the tab settings to the minimum troff state machine 261 */ 262 263void statem::add_tag_ta() 264{ 265 if (is_html) { 266 string s = string(""); 267 hunits d, l; 268 enum tab_type t; 269 do { 270 t = curenv->tabs.distance_to_next_tab(l, &d); 271 l += d; 272 switch (t) { 273 case TAB_LEFT: 274 s += " L "; 275 s += as_string(l.to_units()); 276 break; 277 case TAB_CENTER: 278 s += " C "; 279 s += as_string(l.to_units()); 280 break; 281 case TAB_RIGHT: 282 s += " R "; 283 s += as_string(l.to_units()); 284 break; 285 case TAB_NONE: 286 break; 287 } 288 } while (t != TAB_NONE && l < curenv->get_line_length()); 289 s += '\0'; 290 string_values[MTSM_TA].set(s); 291 } 292} 293 294void statem::update(statem *older, statem *newer, int_value_state t) 295{ 296 if (newer->int_values[t].differs(older->int_values[t]) 297 && !newer->int_values[t].is_known) 298 newer->int_values[t].set(older->int_values[t].value); 299} 300 301void statem::update(statem *older, statem *newer, units_value_state t) 302{ 303 if (newer->units_values[t].differs(older->units_values[t]) 304 && !newer->units_values[t].is_known) 305 newer->units_values[t].set(older->units_values[t].value); 306} 307 308void statem::update(statem *older, statem *newer, bool_value_state t) 309{ 310 if (newer->bool_values[t].differs(older->bool_values[t]) 311 && !newer->bool_values[t].is_known) 312 newer->bool_values[t].set(older->bool_values[t].value); 313} 314 315void statem::update(statem *older, statem *newer, string_value_state t) 316{ 317 if (newer->string_values[t].differs(older->string_values[t]) 318 && !newer->string_values[t].is_known) 319 newer->string_values[t].set(older->string_values[t].value); 320} 321 322void statem::merge(statem *newer, statem *older) 323{ 324 if (newer == 0 || older == 0) 325 return; 326 update(older, newer, MTSM_EOL); 327 update(older, newer, MTSM_BR); 328 update(older, newer, MTSM_FI); 329 update(older, newer, MTSM_LL); 330 update(older, newer, MTSM_PO); 331 update(older, newer, MTSM_RJ); 332 update(older, newer, MTSM_SP); 333 update(older, newer, MTSM_TA); 334 update(older, newer, MTSM_TI); 335 update(older, newer, MTSM_CE); 336} 337 338stack::stack() 339: next(0), state(0) 340{ 341} 342 343stack::stack(statem *s, stack *n) 344: next(n), state(s) 345{ 346} 347 348stack::~stack() 349{ 350 if (state) 351 delete state; 352 if (next) 353 delete next; 354} 355 356mtsm::mtsm() 357: sp(0) 358{ 359 driver = new statem(); 360} 361 362mtsm::~mtsm() 363{ 364 delete driver; 365 if (sp) 366 delete sp; 367} 368 369/* 370 * push_state - push the current troff state and use `n' as 371 * the new troff state. 372 */ 373 374void mtsm::push_state(statem *n) 375{ 376 if (is_html) { 377#if defined(DEBUGGING) 378 if (debug_state) 379 fprintf(stderr, "--> state %d pushed\n", n->issue_no) ; fflush(stderr); 380#endif 381 sp = new stack(n, sp); 382 } 383} 384 385void mtsm::pop_state() 386{ 387 if (is_html) { 388#if defined(DEBUGGING) 389 if (debug_state) 390 fprintf(stderr, "--> state popped\n") ; fflush(stderr); 391#endif 392 if (sp == 0) 393 fatal("empty state machine stack"); 394 if (sp->state) 395 delete sp->state; 396 sp->state = 0; 397 stack *t = sp; 398 sp = sp->next; 399 t->next = 0; 400 delete t; 401 } 402} 403 404/* 405 * inherit - scan the stack and collects inherited values. 406 */ 407 408void mtsm::inherit(statem *s, int reset_bool) 409{ 410 if (sp && sp->state) { 411 if (s->units_values[MTSM_IN].is_known 412 && sp->state->units_values[MTSM_IN].is_known) 413 s->units_values[MTSM_IN].value += sp->state->units_values[MTSM_IN].value; 414 s->update(sp->state, s, MTSM_FI); 415 s->update(sp->state, s, MTSM_LL); 416 s->update(sp->state, s, MTSM_PO); 417 s->update(sp->state, s, MTSM_RJ); 418 s->update(sp->state, s, MTSM_TA); 419 s->update(sp->state, s, MTSM_TI); 420 s->update(sp->state, s, MTSM_CE); 421 if (sp->state->bool_values[MTSM_BR].is_known 422 && sp->state->bool_values[MTSM_BR].value) { 423 if (reset_bool) 424 sp->state->bool_values[MTSM_BR].set(0); 425 s->bool_values[MTSM_BR].set(1); 426 if (debug_state) 427 fprintf(stderr, "inherited br from pushed state %d\n", 428 sp->state->issue_no); 429 } 430 else if (s->bool_values[MTSM_BR].is_known 431 && s->bool_values[MTSM_BR].value) 432 if (! s->int_values[MTSM_CE].is_known) 433 s->bool_values[MTSM_BR].unset(); 434 if (sp->state->bool_values[MTSM_EOL].is_known 435 && sp->state->bool_values[MTSM_EOL].value) { 436 if (reset_bool) 437 sp->state->bool_values[MTSM_EOL].set(0); 438 s->bool_values[MTSM_EOL].set(1); 439 } 440 } 441} 442 443void mtsm::flush(FILE *fp, statem *s, string tag_list) 444{ 445 if (is_html && s) { 446 inherit(s, 1); 447 driver->flush(fp, s); 448 // Set rj, ce, ti to unknown if they were known and 449 // we have seen an eol or br. This ensures that these values 450 // are emitted during the next glyph (as they step from n..0 451 // at each newline). 452 if ((driver->bool_values[MTSM_EOL].is_known 453 && driver->bool_values[MTSM_EOL].value) 454 || (driver->bool_values[MTSM_BR].is_known 455 && driver->bool_values[MTSM_BR].value)) { 456 if (driver->units_values[MTSM_TI].is_known) 457 driver->units_values[MTSM_TI].is_known = 0; 458 if (driver->int_values[MTSM_RJ].is_known 459 && driver->int_values[MTSM_RJ].value > 0) 460 driver->int_values[MTSM_RJ].is_known = 0; 461 if (driver->int_values[MTSM_CE].is_known 462 && driver->int_values[MTSM_CE].value > 0) 463 driver->int_values[MTSM_CE].is_known = 0; 464 } 465 // reset the boolean values 466 driver->bool_values[MTSM_BR].set(0); 467 driver->bool_values[MTSM_EOL].set(0); 468 // reset space value 469 driver->int_values[MTSM_SP].set(0); 470 // lastly write out any direct tag entries 471 if (tag_list != string("")) { 472 string t = tag_list + '\0'; 473 fputs(t.contents(), fp); 474 } 475 } 476} 477 478/* 479 * display_state - dump out a synopsis of the state to stderr. 480 */ 481 482void statem::display_state() 483{ 484 fprintf(stderr, " <state "); 485 if (bool_values[MTSM_BR].is_known) { 486 if (bool_values[MTSM_BR].value) 487 fprintf(stderr, "[br]"); 488 else 489 fprintf(stderr, "[!br]"); 490 } 491 if (bool_values[MTSM_EOL].is_known) { 492 if (bool_values[MTSM_EOL].value) 493 fprintf(stderr, "[eol]"); 494 else 495 fprintf(stderr, "[!eol]"); 496 } 497 if (int_values[MTSM_SP].is_known) { 498 if (int_values[MTSM_SP].value) 499 fprintf(stderr, "[sp %d]", int_values[MTSM_SP].value); 500 else 501 fprintf(stderr, "[!sp]"); 502 } 503 fprintf(stderr, ">"); 504 fflush(stderr); 505} 506 507int mtsm::has_changed(int_value_state t, statem *s) 508{ 509 return driver->int_values[t].differs(s->int_values[t]); 510} 511 512int mtsm::has_changed(units_value_state t, statem *s) 513{ 514 return driver->units_values[t].differs(s->units_values[t]); 515} 516 517int mtsm::has_changed(bool_value_state t, statem *s) 518{ 519 return driver->bool_values[t].differs(s->bool_values[t]); 520} 521 522int mtsm::has_changed(string_value_state t, statem *s) 523{ 524 return driver->string_values[t].differs(s->string_values[t]); 525} 526 527int mtsm::changed(statem *s) 528{ 529 if (s == 0 || !is_html) 530 return 0; 531 s = new statem(s); 532 inherit(s, 0); 533 int result = has_changed(MTSM_EOL, s) 534 || has_changed(MTSM_BR, s) 535 || has_changed(MTSM_FI, s) 536 || has_changed(MTSM_IN, s) 537 || has_changed(MTSM_LL, s) 538 || has_changed(MTSM_PO, s) 539 || has_changed(MTSM_RJ, s) 540 || has_changed(MTSM_SP, s) 541 || has_changed(MTSM_TA, s) 542 || has_changed(MTSM_CE, s); 543 delete s; 544 return result; 545} 546 547void mtsm::add_tag(FILE *fp, string s) 548{ 549 fflush(fp); 550 s += '\0'; 551 fputs(s.contents(), fp); 552} 553 554/* 555 * state_set class 556 */ 557 558state_set::state_set() 559: boolset(0), intset(0), unitsset(0), stringset(0) 560{ 561} 562 563state_set::~state_set() 564{ 565} 566 567void state_set::incl(bool_value_state b) 568{ 569 boolset |= 1 << (int)b; 570} 571 572void state_set::incl(int_value_state i) 573{ 574 intset |= 1 << (int)i; 575} 576 577void state_set::incl(units_value_state u) 578{ 579 unitsset |= 1 << (int)u; 580} 581 582void state_set::incl(string_value_state s) 583{ 584 stringset |= 1 << (int)s; 585} 586 587void state_set::excl(bool_value_state b) 588{ 589 boolset &= ~(1 << (int)b); 590} 591 592void state_set::excl(int_value_state i) 593{ 594 intset &= ~(1 << (int)i); 595} 596 597void state_set::excl(units_value_state u) 598{ 599 unitsset &= ~(1 << (int)u); 600} 601 602void state_set::excl(string_value_state s) 603{ 604 stringset &= ~(1 << (int)s); 605} 606 607int state_set::is_in(bool_value_state b) 608{ 609 return (boolset & (1 << (int)b)) != 0; 610} 611 612int state_set::is_in(int_value_state i) 613{ 614 return (intset & (1 << (int)i)) != 0; 615} 616 617// Note: this used to have a bug s.t. it always tested for bit 0 (benl 18/5/11) 618int state_set::is_in(units_value_state u) 619{ 620 return (unitsset & (1 << (int)u)) != 0; 621} 622 623// Note: this used to have a bug s.t. it always tested for bit 0 (benl 18/5/11) 624int state_set::is_in(string_value_state s) 625{ 626 return (stringset & (1 << (int)s)) != 0; 627} 628 629void state_set::add(units_value_state, int n) 630{ 631 unitsset += n; 632} 633 634units state_set::val(units_value_state) 635{ 636 return unitsset; 637} 638