Initial import of binutils 2.22 on the new vendor branch
[dragonfly.git] / contrib / groff / src / roff / troff / mtsm.cpp
1 // -*- C++ -*-
2 /* Copyright (C) 2003, 2004, 2009 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
32 int_value::int_value()
33 : value(0), is_known(0)
34 {
35 }
36
37 int_value::~int_value()
38 {
39 }
40
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
56 void int_value::set(int v)
57 {
58   is_known = 1;
59   value = v;
60 }
61
62 void int_value::unset()
63 {
64   is_known = 0;
65 }
66
67 void int_value::set_if_unknown(int v)
68 {
69   if (!is_known)
70     set(v);
71 }
72
73 int int_value::differs(int_value compare)
74 {
75   return compare.is_known
76          && (!is_known || value != compare.value);
77 }
78
79 bool_value::bool_value()
80 {
81 }
82
83 bool_value::~bool_value()
84 {
85 }
86
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
100 units_value::units_value()
101 {
102 }
103
104 units_value::~units_value()
105 {
106 }
107
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
123 void units_value::set(hunits v)
124 {
125   is_known = 1;
126   value = v.to_units();
127 }
128
129 int units_value::differs(units_value compare)
130 {
131   return compare.is_known
132          && (!is_known || value != compare.value);
133 }
134
135 string_value::string_value()
136 : value(string("")), is_known(0)
137 {
138 }
139
140 string_value::~string_value()
141 {
142 }
143
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
157 void string_value::set(string v)
158 {
159   is_known = 1;
160   value = v;
161 }
162
163 void string_value::unset()
164 {
165   is_known = 0;
166 }
167
168 int string_value::differs(string_value compare)
169 {
170   return compare.is_known
171          && (!is_known || value != compare.value);
172 }
173
174 statem::statem()
175 {
176   issue_no = no_of_statems;
177   no_of_statems++;
178 }
179
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
194 statem::~statem()
195 {
196 }
197
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
228 void statem::add_tag(int_value_state t, int v)
229 {
230   int_values[t].set(v);
231 }
232
233 void statem::add_tag(units_value_state t, hunits v)
234 {
235   units_values[t].set(v);
236 }
237
238 void statem::add_tag(bool_value_state t)
239 {
240   bool_values[t].set(1);
241 }
242
243 void statem::add_tag(string_value_state t, string v)
244 {
245   string_values[t].set(v);
246 }
247
248 void statem::add_tag_if_unknown(int_value_state t, int v)
249 {
250   int_values[t].set_if_unknown(v);
251 }
252
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
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
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
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
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
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
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
337 stack::stack()
338 : next(0), state(0)
339 {
340 }
341
342 stack::stack(statem *s, stack *n)
343 : next(n), state(s)
344 {
345 }
346
347 stack::~stack()
348 {
349   if (state)
350     delete state;
351   if (next)
352     delete next;
353 }
354
355 mtsm::mtsm()
356 : sp(0)
357 {
358   driver = new statem();
359 }
360
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
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) ; fflush(stderr);
379 #endif
380     sp = new stack(n, sp);
381   }
382 }
383
384 void 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
407 void 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
442 void 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
481 void 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
503 int mtsm::has_changed(int_value_state t, statem *s)
504 {
505   return driver->int_values[t].differs(s->int_values[t]);
506 }
507
508 int mtsm::has_changed(units_value_state t, statem *s)
509 {
510   return driver->units_values[t].differs(s->units_values[t]);
511 }
512
513 int mtsm::has_changed(bool_value_state t, statem *s)
514 {
515   return driver->bool_values[t].differs(s->bool_values[t]);
516 }
517
518 int mtsm::has_changed(string_value_state t, statem *s)
519 {
520   return driver->string_values[t].differs(s->string_values[t]);
521 }
522
523 int 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
543 void 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
554 state_set::state_set()
555 : boolset(0), intset(0), unitsset(0), stringset(0)
556 {
557 }
558
559 state_set::~state_set()
560 {
561 }
562
563 void state_set::incl(bool_value_state b)
564 {
565   boolset |= 1 << (int)b;
566 }
567
568 void state_set::incl(int_value_state i)
569 {
570   intset |= 1 << (int)i;
571 }
572
573 void state_set::incl(units_value_state u)
574 {
575   unitsset |= 1 << (int)u;
576 }
577
578 void state_set::incl(string_value_state s)
579 {
580   stringset |= 1 << (int)s;
581 }
582
583 void state_set::excl(bool_value_state b)
584 {
585   boolset &= ~(1 << (int)b);
586 }
587
588 void state_set::excl(int_value_state i)
589 {
590   intset &= ~(1 << (int)i);
591 }
592
593 void state_set::excl(units_value_state u)
594 {
595   unitsset &= ~(1 << (int)u);
596 }
597
598 void state_set::excl(string_value_state s)
599 {
600   stringset &= ~(1 << (int)s);
601 }
602
603 int state_set::is_in(bool_value_state b)
604 {
605   return (boolset & (1 << (int)b)) != 0;
606 }
607
608 int state_set::is_in(int_value_state i)
609 {
610   return (intset & (1 << (int)i)) != 0;
611 }
612
613 int state_set::is_in(units_value_state u)
614 {
615   return (unitsset & (1 << (int)u) != 0);
616 }
617
618 int state_set::is_in(string_value_state s)
619 {
620   return (stringset & (1 << (int)s) != 0);
621 }
622
623 void state_set::add(units_value_state, int n)
624 {
625   unitsset += n;
626 }
627
628 units state_set::val(units_value_state)
629 {
630   return unitsset;
631 }