groff: update vendor branch to v1.20.1
[dragonfly.git] / contrib / groff / src / roff / troff / mtsm.cpp
CommitLineData
465b256c 1// -*- C++ -*-
4d3e9548 2/* Copyright (C) 2003, 2004, 2009 Free Software Foundation, Inc.
465b256c
JR
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
4d3e9548
JL
9Software Foundation, either version 3 of the License, or
10(at your option) any later version.
465b256c
JR
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
4d3e9548
JL
17You should have received a copy of the GNU General Public License
18along with this program. If not, see <http://www.gnu.org/licenses/>. */
465b256c
JR
19
20#define DEBUGGING
21
22extern 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
30static int no_of_statems = 0; // debugging aid
31
32int_value::int_value()
33: value(0), is_known(0)
34{
35}
36
37int_value::~int_value()
38{
39}
40
41void 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
56void int_value::set(int v)
57{
58 is_known = 1;
59 value = v;
60}
61
62void int_value::unset()
63{
64 is_known = 0;
65}
66
67void int_value::set_if_unknown(int v)
68{
69 if (!is_known)
70 set(v);
71}
72
73int int_value::differs(int_value compare)
74{
75 return compare.is_known
76 && (!is_known || value != compare.value);
77}
78
79bool_value::bool_value()
80{
81}
82
83bool_value::~bool_value()
84{
85}
86
87void 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
100units_value::units_value()
101{
102}
103
104units_value::~units_value()
105{
106}
107
108void 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
123void units_value::set(hunits v)
124{
125 is_known = 1;
126 value = v.to_units();
127}
128
129int units_value::differs(units_value compare)
130{
131 return compare.is_known
132 && (!is_known || value != compare.value);
133}
134
135string_value::string_value()
136: value(string("")), is_known(0)
137{
138}
139
140string_value::~string_value()
141{
142}
143
144void 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
157void string_value::set(string v)
158{
159 is_known = 1;
160 value = v;
161}
162
163void string_value::unset()
164{
165 is_known = 0;
166}
167
168int string_value::differs(string_value compare)
169{
170 return compare.is_known
171 && (!is_known || value != compare.value);
172}
173
174statem::statem()
175{
176 issue_no = no_of_statems;
177 no_of_statems++;
178}
179
180statem::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
194statem::~statem()
195{
196}
197
198void 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
228void statem::add_tag(int_value_state t, int v)
229{
230 int_values[t].set(v);
231}
232
233void statem::add_tag(units_value_state t, hunits v)
234{
235 units_values[t].set(v);
236}
237
238void statem::add_tag(bool_value_state t)
239{
240 bool_values[t].set(1);
241}
242
243void statem::add_tag(string_value_state t, string v)
244{
245 string_values[t].set(v);
246}
247
248void statem::add_tag_if_unknown(int_value_state t, int v)
249{
250 int_values[t].set_if_unknown(v);
251}
252
253void 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
262void 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
293void 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
300void 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
307void 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
314void 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
321void 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
337stack::stack()
338: next(0), state(0)
339{
340}
341
342stack::stack(statem *s, stack *n)
343: next(n), state(s)
344{
345}
346
347stack::~stack()
348{
349 if (state)
350 delete state;
351 if (next)
352 delete next;
353}
354
355mtsm::mtsm()
356: sp(0)
357{
358 driver = new statem();
359}
360
361mtsm::~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
373void 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) ; fflush(stderr);
379#endif
380 sp = new stack(n, sp);
381 }
382}
383
384void mtsm::pop_state()
385{
386 if (is_html) {
387#if defined(DEBUGGING)
388 if (debug_state)
389 fprintf(stderr, "--> state popped\n") ; fflush(stderr);
390#endif
391 if (sp == 0)
392 fatal("empty state machine stack");
393 if (sp->state)
394 delete sp->state;
395 sp->state = 0;
396 stack *t = sp;
397 sp = sp->next;
398 t->next = 0;
399 delete t;
400 }
401}
402
403/*
404 * inherit - scan the stack and collects inherited values.
405 */
406
407void mtsm::inherit(statem *s, int reset_bool)
408{
409 if (sp && sp->state) {
410 if (s->units_values[MTSM_IN].is_known
411 && sp->state->units_values[MTSM_IN].is_known)
412 s->units_values[MTSM_IN].value += sp->state->units_values[MTSM_IN].value;
413 s->update(sp->state, s, MTSM_FI);
414 s->update(sp->state, s, MTSM_LL);
415 s->update(sp->state, s, MTSM_PO);
416 s->update(sp->state, s, MTSM_RJ);
417 s->update(sp->state, s, MTSM_TA);
418 s->update(sp->state, s, MTSM_TI);
419 s->update(sp->state, s, MTSM_CE);
420 if (sp->state->bool_values[MTSM_BR].is_known
421 && sp->state->bool_values[MTSM_BR].value) {
422 if (reset_bool)
423 sp->state->bool_values[MTSM_BR].set(0);
424 s->bool_values[MTSM_BR].set(1);
425 if (debug_state)
426 fprintf(stderr, "inherited br from pushed state %d\n",
427 sp->state->issue_no);
428 }
429 else if (s->bool_values[MTSM_BR].is_known
430 && s->bool_values[MTSM_BR].value)
431 if (! s->int_values[MTSM_CE].is_known)
432 s->bool_values[MTSM_BR].unset();
433 if (sp->state->bool_values[MTSM_EOL].is_known
434 && sp->state->bool_values[MTSM_EOL].value) {
435 if (reset_bool)
436 sp->state->bool_values[MTSM_EOL].set(0);
437 s->bool_values[MTSM_EOL].set(1);
438 }
439 }
440}
441
442void mtsm::flush(FILE *fp, statem *s, string tag_list)
443{
444 if (is_html && s) {
445 inherit(s, 1);
446 driver->flush(fp, s);
447 // Set rj, ce, ti to unknown if they were known and
448 // we have seen an eol or br. This ensures that these values
449 // are emitted during the next glyph (as they step from n..0
450 // at each newline).
451 if ((driver->bool_values[MTSM_EOL].is_known
452 && driver->bool_values[MTSM_EOL].value)
453 || (driver->bool_values[MTSM_BR].is_known
454 && driver->bool_values[MTSM_BR].value)) {
455 if (driver->units_values[MTSM_TI].is_known)
456 driver->units_values[MTSM_TI].is_known = 0;
457 if (driver->int_values[MTSM_RJ].is_known
458 && driver->int_values[MTSM_RJ].value > 0)
459 driver->int_values[MTSM_RJ].is_known = 0;
460 if (driver->int_values[MTSM_CE].is_known
461 && driver->int_values[MTSM_CE].value > 0)
462 driver->int_values[MTSM_CE].is_known = 0;
463 }
464 // reset the boolean values
465 driver->bool_values[MTSM_BR].set(0);
466 driver->bool_values[MTSM_EOL].set(0);
467 // reset space value
468 driver->int_values[MTSM_SP].set(0);
469 // lastly write out any direct tag entries
470 if (tag_list != string("")) {
471 string t = tag_list + '\0';
472 fputs(t.contents(), fp);
473 }
474 }
475}
476
477/*
478 * display_state - dump out a synopsis of the state to stderr.
479 */
480
481void statem::display_state()
482{
483 fprintf(stderr, " <state ");
484 if (bool_values[MTSM_BR].is_known)
485 if (bool_values[MTSM_BR].value)
486 fprintf(stderr, "[br]");
487 else
488 fprintf(stderr, "[!br]");
489 if (bool_values[MTSM_EOL].is_known)
490 if (bool_values[MTSM_EOL].value)
491 fprintf(stderr, "[eol]");
492 else
493 fprintf(stderr, "[!eol]");
494 if (int_values[MTSM_SP].is_known)
495 if (int_values[MTSM_SP].value)
496 fprintf(stderr, "[sp %d]", int_values[MTSM_SP].value);
497 else
498 fprintf(stderr, "[!sp]");
499 fprintf(stderr, ">");
500 fflush(stderr);
501}
502
503int mtsm::has_changed(int_value_state t, statem *s)
504{
505 return driver->int_values[t].differs(s->int_values[t]);
506}
507
508int mtsm::has_changed(units_value_state t, statem *s)
509{
510 return driver->units_values[t].differs(s->units_values[t]);
511}
512
513int mtsm::has_changed(bool_value_state t, statem *s)
514{
515 return driver->bool_values[t].differs(s->bool_values[t]);
516}
517
518int mtsm::has_changed(string_value_state t, statem *s)
519{
520 return driver->string_values[t].differs(s->string_values[t]);
521}
522
523int mtsm::changed(statem *s)
524{
525 if (s == 0 || !is_html)
526 return 0;
527 s = new statem(s);
528 inherit(s, 0);
529 int result = has_changed(MTSM_EOL, s)
530 || has_changed(MTSM_BR, s)
531 || has_changed(MTSM_FI, s)
532 || has_changed(MTSM_IN, s)
533 || has_changed(MTSM_LL, s)
534 || has_changed(MTSM_PO, s)
535 || has_changed(MTSM_RJ, s)
536 || has_changed(MTSM_SP, s)
537 || has_changed(MTSM_TA, s)
538 || has_changed(MTSM_CE, s);
539 delete s;
540 return result;
541}
542
543void mtsm::add_tag(FILE *fp, string s)
544{
545 fflush(fp);
546 s += '\0';
547 fputs(s.contents(), fp);
548}
549
550/*
551 * state_set class
552 */
553
554state_set::state_set()
555: boolset(0), intset(0), unitsset(0), stringset(0)
556{
557}
558
559state_set::~state_set()
560{
561}
562
563void state_set::incl(bool_value_state b)
564{
565 boolset |= 1 << (int)b;
566}
567
568void state_set::incl(int_value_state i)
569{
570 intset |= 1 << (int)i;
571}
572
573void state_set::incl(units_value_state u)
574{
575 unitsset |= 1 << (int)u;
576}
577
578void state_set::incl(string_value_state s)
579{
580 stringset |= 1 << (int)s;
581}
582
583void state_set::excl(bool_value_state b)
584{
585 boolset &= ~(1 << (int)b);
586}
587
588void state_set::excl(int_value_state i)
589{
590 intset &= ~(1 << (int)i);
591}
592
593void state_set::excl(units_value_state u)
594{
595 unitsset &= ~(1 << (int)u);
596}
597
598void state_set::excl(string_value_state s)
599{
600 stringset &= ~(1 << (int)s);
601}
602
603int state_set::is_in(bool_value_state b)
604{
605 return (boolset & (1 << (int)b)) != 0;
606}
607
608int state_set::is_in(int_value_state i)
609{
610 return (intset & (1 << (int)i)) != 0;
611}
612
613int state_set::is_in(units_value_state u)
614{
615 return (unitsset & (1 << (int)u) != 0);
616}
617
618int state_set::is_in(string_value_state s)
619{
620 return (stringset & (1 << (int)s) != 0);
621}
622
623void state_set::add(units_value_state, int n)
624{
625 unitsset += n;
626}
627
628units state_set::val(units_value_state)
629{
630 return unitsset;
631}