xref: /netbsd-src/external/gpl2/groff/dist/src/roff/troff/mtsm.cpp (revision 4acc5b6b2013c23d840d952be7c84bc64d81149a)
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 
int_value()35 int_value::int_value()
36 : value(0), is_known(0)
37 {
38 }
39 
~int_value()40 int_value::~int_value()
41 {
42 }
43 
diff(FILE * fp,const char * s,int_value compare)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 
set(int v)59 void int_value::set(int v)
60 {
61   is_known = 1;
62   value = v;
63 }
64 
unset()65 void int_value::unset()
66 {
67   is_known = 0;
68 }
69 
set_if_unknown(int v)70 void int_value::set_if_unknown(int v)
71 {
72   if (!is_known)
73     set(v);
74 }
75 
differs(int_value compare)76 int int_value::differs(int_value compare)
77 {
78   return compare.is_known
79 	 && (!is_known || value != compare.value);
80 }
81 
bool_value()82 bool_value::bool_value()
83 {
84 }
85 
~bool_value()86 bool_value::~bool_value()
87 {
88 }
89 
diff(FILE * fp,const char * s,bool_value compare)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 
units_value()103 units_value::units_value()
104 {
105 }
106 
~units_value()107 units_value::~units_value()
108 {
109 }
110 
diff(FILE * fp,const char * s,units_value compare)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 
set(hunits v)126 void units_value::set(hunits v)
127 {
128   is_known = 1;
129   value = v.to_units();
130 }
131 
differs(units_value compare)132 int units_value::differs(units_value compare)
133 {
134   return compare.is_known
135 	 && (!is_known || value != compare.value);
136 }
137 
string_value()138 string_value::string_value()
139 : value(string("")), is_known(0)
140 {
141 }
142 
~string_value()143 string_value::~string_value()
144 {
145 }
146 
diff(FILE * fp,const char * s,string_value compare)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 
set(string v)160 void string_value::set(string v)
161 {
162   is_known = 1;
163   value = v;
164 }
165 
unset()166 void string_value::unset()
167 {
168   is_known = 0;
169 }
170 
differs(string_value compare)171 int string_value::differs(string_value compare)
172 {
173   return compare.is_known
174 	 && (!is_known || value != compare.value);
175 }
176 
statem()177 statem::statem()
178 {
179   issue_no = no_of_statems;
180   no_of_statems++;
181 }
182 
statem(statem * copy)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 
~statem()197 statem::~statem()
198 {
199 }
200 
flush(FILE * fp,statem * compare)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 
add_tag(int_value_state t,int v)231 void statem::add_tag(int_value_state t, int v)
232 {
233   int_values[t].set(v);
234 }
235 
add_tag(units_value_state t,hunits v)236 void statem::add_tag(units_value_state t, hunits v)
237 {
238   units_values[t].set(v);
239 }
240 
add_tag(bool_value_state t)241 void statem::add_tag(bool_value_state t)
242 {
243   bool_values[t].set(1);
244 }
245 
add_tag(string_value_state t,string v)246 void statem::add_tag(string_value_state t, string v)
247 {
248   string_values[t].set(v);
249 }
250 
add_tag_if_unknown(int_value_state t,int v)251 void statem::add_tag_if_unknown(int_value_state t, int v)
252 {
253   int_values[t].set_if_unknown(v);
254 }
255 
sub_tag_ce()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 
add_tag_ta()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 
update(statem * older,statem * newer,int_value_state t)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 
update(statem * older,statem * newer,units_value_state t)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 
update(statem * older,statem * newer,bool_value_state t)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 
update(statem * older,statem * newer,string_value_state t)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 
merge(statem * newer,statem * older)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 
stack()340 stack::stack()
341 : next(0), state(0)
342 {
343 }
344 
stack(statem * s,stack * n)345 stack::stack(statem *s, stack *n)
346 : next(n), state(s)
347 {
348 }
349 
~stack()350 stack::~stack()
351 {
352   if (state)
353     delete state;
354   if (next)
355     delete next;
356 }
357 
mtsm()358 mtsm::mtsm()
359 : sp(0)
360 {
361   driver = new statem();
362 }
363 
~mtsm()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 
push_state(statem * n)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 
pop_state()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 
inherit(statem * s,int reset_bool)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 
flush(FILE * fp,statem * s,string tag_list)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 
display_state()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 
has_changed(int_value_state t,statem * s)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 
has_changed(units_value_state t,statem * s)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 
has_changed(bool_value_state t,statem * s)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 
has_changed(string_value_state t,statem * s)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 
changed(statem * s)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 
add_tag(FILE * fp,string s)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 
state_set()560 state_set::state_set()
561 : boolset(0), intset(0), unitsset(0), stringset(0)
562 {
563 }
564 
~state_set()565 state_set::~state_set()
566 {
567 }
568 
incl(bool_value_state b)569 void state_set::incl(bool_value_state b)
570 {
571   boolset |= 1 << (int)b;
572 }
573 
incl(int_value_state i)574 void state_set::incl(int_value_state i)
575 {
576   intset |= 1 << (int)i;
577 }
578 
incl(units_value_state u)579 void state_set::incl(units_value_state u)
580 {
581   unitsset |= 1 << (int)u;
582 }
583 
incl(string_value_state s)584 void state_set::incl(string_value_state s)
585 {
586   stringset |= 1 << (int)s;
587 }
588 
excl(bool_value_state b)589 void state_set::excl(bool_value_state b)
590 {
591   boolset &= ~(1 << (int)b);
592 }
593 
excl(int_value_state i)594 void state_set::excl(int_value_state i)
595 {
596   intset &= ~(1 << (int)i);
597 }
598 
excl(units_value_state u)599 void state_set::excl(units_value_state u)
600 {
601   unitsset &= ~(1 << (int)u);
602 }
603 
excl(string_value_state s)604 void state_set::excl(string_value_state s)
605 {
606   stringset &= ~(1 << (int)s);
607 }
608 
is_in(bool_value_state b)609 int state_set::is_in(bool_value_state b)
610 {
611   return (boolset & (1 << (int)b)) != 0;
612 }
613 
is_in(int_value_state i)614 int state_set::is_in(int_value_state i)
615 {
616   return (intset & (1 << (int)i)) != 0;
617 }
618 
is_in(units_value_state u)619 int state_set::is_in(units_value_state u)
620 {
621   return ((unitsset & (1 << (int)u)) != 0);
622 }
623 
is_in(string_value_state s)624 int state_set::is_in(string_value_state s)
625 {
626   return ((stringset & (1 << (int)s)) != 0);
627 }
628 
add(units_value_state,int n)629 void state_set::add(units_value_state, int n)
630 {
631   unitsset += n;
632 }
633 
val(units_value_state)634 units state_set::val(units_value_state)
635 {
636   return unitsset;
637 }
638