PageRenderTime 67ms CodeModel.GetById 17ms app.highlight 44ms RepoModel.GetById 1ms app.codeStats 0ms

/contrib/groff/src/roff/troff/mtsm.cpp

https://bitbucket.org/freebsd/freebsd-head/
C++ | 634 lines | 512 code | 78 blank | 44 comment | 99 complexity | 7ca2cee6a3dedac4a9cb024f59cfaa1f MD5 | raw file
  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  if (bool_values[MTSM_EOL].is_known)
491    if (bool_values[MTSM_EOL].value)
492      fprintf(stderr, "[eol]");
493    else
494      fprintf(stderr, "[!eol]");
495  if (int_values[MTSM_SP].is_known)
496    if (int_values[MTSM_SP].value)
497      fprintf(stderr, "[sp %d]", int_values[MTSM_SP].value);
498    else
499      fprintf(stderr, "[!sp]");
500  fprintf(stderr, ">");
501  fflush(stderr);
502}
503
504int mtsm::has_changed(int_value_state t, statem *s)
505{
506  return driver->int_values[t].differs(s->int_values[t]);
507}
508
509int mtsm::has_changed(units_value_state t, statem *s)
510{
511  return driver->units_values[t].differs(s->units_values[t]);
512}
513
514int mtsm::has_changed(bool_value_state t, statem *s)
515{
516  return driver->bool_values[t].differs(s->bool_values[t]);
517}
518
519int mtsm::has_changed(string_value_state t, statem *s)
520{
521  return driver->string_values[t].differs(s->string_values[t]);
522}
523
524int mtsm::changed(statem *s)
525{
526  if (s == 0 || !is_html)
527    return 0;
528  s = new statem(s);
529  inherit(s, 0);
530  int result = has_changed(MTSM_EOL, s)
531	       || has_changed(MTSM_BR, s)
532	       || has_changed(MTSM_FI, s)
533	       || has_changed(MTSM_IN, s)
534	       || has_changed(MTSM_LL, s)
535	       || has_changed(MTSM_PO, s)
536	       || has_changed(MTSM_RJ, s)
537	       || has_changed(MTSM_SP, s)
538	       || has_changed(MTSM_TA, s)
539	       || has_changed(MTSM_CE, s);
540  delete s;
541  return result;
542}
543
544void mtsm::add_tag(FILE *fp, string s)
545{
546  fflush(fp);
547  s += '\0';
548  fputs(s.contents(), fp);
549}
550
551/*
552 *  state_set class
553 */
554
555state_set::state_set()
556: boolset(0), intset(0), unitsset(0), stringset(0)
557{
558}
559
560state_set::~state_set()
561{
562}
563
564void state_set::incl(bool_value_state b)
565{
566  boolset |= 1 << (int)b;
567}
568
569void state_set::incl(int_value_state i)
570{
571  intset |= 1 << (int)i;
572}
573
574void state_set::incl(units_value_state u)
575{
576  unitsset |= 1 << (int)u;
577}
578
579void state_set::incl(string_value_state s)
580{
581  stringset |= 1 << (int)s;
582}
583
584void state_set::excl(bool_value_state b)
585{
586  boolset &= ~(1 << (int)b);
587}
588
589void state_set::excl(int_value_state i)
590{
591  intset &= ~(1 << (int)i);
592}
593
594void state_set::excl(units_value_state u)
595{
596  unitsset &= ~(1 << (int)u);
597}
598
599void state_set::excl(string_value_state s)
600{
601  stringset &= ~(1 << (int)s);
602}
603
604int state_set::is_in(bool_value_state b)
605{
606  return (boolset & (1 << (int)b)) != 0;
607}
608
609int state_set::is_in(int_value_state i)
610{
611  return (intset & (1 << (int)i)) != 0;
612}
613
614// Note: this used to have a bug s.t. it always tested for bit 0 (benl 18/5/11)
615int state_set::is_in(units_value_state u)
616{
617  return (unitsset & (1 << (int)u)) != 0;
618}
619
620// Note: this used to have a bug s.t. it always tested for bit 0 (benl 18/5/11)
621int state_set::is_in(string_value_state s)
622{
623  return (stringset & (1 << (int)s)) != 0;
624}
625
626void state_set::add(units_value_state, int n)
627{
628  unitsset += n;
629}
630
631units state_set::val(units_value_state)
632{
633  return unitsset;
634}