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