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