Update to groff 1.19.2.
[dragonfly.git] / contrib / groff-1.19 / src / roff / troff / mtsm.cpp
1 // -*- C++ -*-
2 /* Copyright (C) 2003, 2004 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 2, or (at your option) any later
10 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 along
18 with groff; see the file COPYING.  If not, write to the Free Software
19 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
20
21 #define DEBUGGING
22
23 extern 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
31 static int no_of_statems = 0;   // debugging aid
32
33 int_value::int_value()
34 : value(0), is_known(0)
35 {
36 }
37
38 int_value::~int_value()
39 {
40 }
41
42 void 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
57 void int_value::set(int v)
58 {
59   is_known = 1;
60   value = v;
61 }
62
63 void int_value::unset()
64 {
65   is_known = 0;
66 }
67
68 void int_value::set_if_unknown(int v)
69 {
70   if (!is_known)
71     set(v);
72 }
73
74 int int_value::differs(int_value compare)
75 {
76   return compare.is_known
77          && (!is_known || value != compare.value);
78 }
79
80 bool_value::bool_value()
81 {
82 }
83
84 bool_value::~bool_value()
85 {
86 }
87
88 void 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
101 units_value::units_value()
102 {
103 }
104
105 units_value::~units_value()
106 {
107 }
108
109 void 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
124 void units_value::set(hunits v)
125 {
126   is_known = 1;
127   value = v.to_units();
128 }
129
130 int units_value::differs(units_value compare)
131 {
132   return compare.is_known
133          && (!is_known || value != compare.value);
134 }
135
136 string_value::string_value()
137 : value(string("")), is_known(0)
138 {
139 }
140
141 string_value::~string_value()
142 {
143 }
144
145 void 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
158 void string_value::set(string v)
159 {
160   is_known = 1;
161   value = v;
162 }
163
164 void string_value::unset()
165 {
166   is_known = 0;
167 }
168
169 int string_value::differs(string_value compare)
170 {
171   return compare.is_known
172          && (!is_known || value != compare.value);
173 }
174
175 statem::statem()
176 {
177   issue_no = no_of_statems;
178   no_of_statems++;
179 }
180
181 statem::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
195 statem::~statem()
196 {
197 }
198
199 void 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
229 void statem::add_tag(int_value_state t, int v)
230 {
231   int_values[t].set(v);
232 }
233
234 void statem::add_tag(units_value_state t, hunits v)
235 {
236   units_values[t].set(v);
237 }
238
239 void statem::add_tag(bool_value_state t)
240 {
241   bool_values[t].set(1);
242 }
243
244 void statem::add_tag(string_value_state t, string v)
245 {
246   string_values[t].set(v);
247 }
248
249 void statem::add_tag_if_unknown(int_value_state t, int v)
250 {
251   int_values[t].set_if_unknown(v);
252 }
253
254 void 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
263 void 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
294 void 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
301 void 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
308 void 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
315 void 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
322 void 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
338 stack::stack()
339 : next(0), state(0)
340 {
341 }
342
343 stack::stack(statem *s, stack *n)
344 : next(n), state(s)
345 {
346 }
347
348 stack::~stack()
349 {
350   if (state)
351     delete state;
352   if (next)
353     delete next;
354 }
355
356 mtsm::mtsm()
357 : sp(0)
358 {
359   driver = new statem();
360 }
361
362 mtsm::~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
374 void 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
385 void 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
408 void 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
443 void 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
482 void 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
504 int mtsm::has_changed(int_value_state t, statem *s)
505 {
506   return driver->int_values[t].differs(s->int_values[t]);
507 }
508
509 int mtsm::has_changed(units_value_state t, statem *s)
510 {
511   return driver->units_values[t].differs(s->units_values[t]);
512 }
513
514 int mtsm::has_changed(bool_value_state t, statem *s)
515 {
516   return driver->bool_values[t].differs(s->bool_values[t]);
517 }
518
519 int mtsm::has_changed(string_value_state t, statem *s)
520 {
521   return driver->string_values[t].differs(s->string_values[t]);
522 }
523
524 int 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
544 void 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
555 state_set::state_set()
556 : boolset(0), intset(0), unitsset(0), stringset(0)
557 {
558 }
559
560 state_set::~state_set()
561 {
562 }
563
564 void state_set::incl(bool_value_state b)
565 {
566   boolset |= 1 << (int)b;
567 }
568
569 void state_set::incl(int_value_state i)
570 {
571   intset |= 1 << (int)i;
572 }
573
574 void state_set::incl(units_value_state u)
575 {
576   unitsset |= 1 << (int)u;
577 }
578
579 void state_set::incl(string_value_state s)
580 {
581   stringset |= 1 << (int)s;
582 }
583
584 void state_set::excl(bool_value_state b)
585 {
586   boolset &= ~(1 << (int)b);
587 }
588
589 void state_set::excl(int_value_state i)
590 {
591   intset &= ~(1 << (int)i);
592 }
593
594 void state_set::excl(units_value_state u)
595 {
596   unitsset &= ~(1 << (int)u);
597 }
598
599 void state_set::excl(string_value_state s)
600 {
601   stringset &= ~(1 << (int)s);
602 }
603
604 int state_set::is_in(bool_value_state b)
605 {
606   return (boolset & (1 << (int)b)) != 0;
607 }
608
609 int state_set::is_in(int_value_state i)
610 {
611   return (intset & (1 << (int)i)) != 0;
612 }
613
614 int state_set::is_in(units_value_state u)
615 {
616   return (unitsset & (1 << (int)u) != 0);
617 }
618
619 int state_set::is_in(string_value_state s)
620 {
621   return (stringset & (1 << (int)s) != 0);
622 }
623
624 void state_set::add(units_value_state, int n)
625 {
626   unitsset += n;
627 }
628
629 units state_set::val(units_value_state)
630 {
631   return unitsset;
632 }