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