Home | History | Annotate | Line # | Download | only in troff
      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 (at) 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