Initial import of binutils 2.22 on the new vendor branch
[dragonfly.git] / contrib / groff / src / devices / grohtml / html-text.cpp
1 // -*- C++ -*-
2 /* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2007, 2009
3  * Free Software Foundation, Inc.
4  *
5  *  Gaius Mulley (gaius@glam.ac.uk) wrote html-text.cpp
6  *
7  *  html-text.cpp
8  *
9  *  provide a troff like state machine interface which
10  *  generates html text.
11  */
12
13 /*
14 This file is part of groff.
15
16 groff is free software; you can redistribute it and/or modify it under
17 the terms of the GNU General Public License as published by the Free
18 Software Foundation, either version 3 of the License, or
19 (at your option) any later version.
20
21 groff is distributed in the hope that it will be useful, but WITHOUT ANY
22 WARRANTY; without even the implied warranty of MERCHANTABILITY or
23 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
24 for more details.
25
26 You should have received a copy of the GNU General Public License
27 along with this program. If not, see <http://www.gnu.org/licenses/>. */
28
29 #include "driver.h"
30 #include "stringclass.h"
31 #include "cset.h"
32
33 #if !defined(TRUE)
34 #   define TRUE  (1==1)
35 #endif
36 #if !defined(FALSE)
37 #   define FALSE (1==0)
38 #endif
39
40
41 #include "html-text.h"
42
43 #undef DEBUGGING
44 // #define DEBUGGING
45
46 html_text::html_text (simple_output *op, html_dialect d) :
47   stackptr(NULL), lastptr(NULL), out(op), dialect(d),
48   space_emitted(TRUE), current_indentation(-1),
49   pageoffset(-1), linelength(-1), blank_para(TRUE),
50   start_space(FALSE)
51 {
52 }
53
54 html_text::~html_text ()
55 {
56   flush_text();
57 }
58
59
60 #if defined(DEBUGGING)
61 static int debugStack = FALSE;
62
63
64 /*
65  *  turnDebug - flip the debugStack boolean and return the new value.
66  */
67
68 static int turnDebug (void)
69 {
70   debugStack = 1-debugStack;
71   return debugStack;
72 }
73
74 /*
75  *  dump_stack_element - display an element of the html stack, p.
76  */
77
78 void html_text::dump_stack_element (tag_definition *p)
79 {
80   fprintf(stderr, " | ");
81   switch (p->type) {
82
83   case P_TAG:      if (p->indent == NULL) {
84                       fprintf(stderr, "<P %s>", (char *)p->arg1); break;
85                    } else {
86                       fprintf(stderr, "<P %s [TABLE]>", (char *)p->arg1); break;
87                    }
88   case I_TAG:      fprintf(stderr, "<I>"); break;
89   case B_TAG:      fprintf(stderr, "<B>"); break;
90   case SUB_TAG:    fprintf(stderr, "<SUB>"); break;
91   case SUP_TAG:    fprintf(stderr, "<SUP>"); break;
92   case TT_TAG:     fprintf(stderr, "<TT>"); break;
93   case PRE_TAG:    if (p->indent == NULL) {
94                       fprintf(stderr, "<PRE>"); break;
95                    } else {
96                       fprintf(stderr, "<PRE [TABLE]>"); break;
97                    }
98   case SMALL_TAG:  fprintf(stderr, "<SMALL>"); break;
99   case BIG_TAG:    fprintf(stderr, "<BIG>"); break;
100   case BREAK_TAG:  fprintf(stderr, "<BREAK>"); break;
101   case COLOR_TAG:  {
102     if (p->col.is_default())
103       fprintf(stderr, "<COLOR (default)>");
104     else {
105       unsigned int r, g, b;
106       
107       p->col.get_rgb(&r, &g, &b);
108       fprintf(stderr, "<COLOR %x %x %x>", r/0x101, g/0x101, b/0x101);
109     }
110     break;
111   }
112   default: fprintf(stderr, "unknown tag");
113   }
114   if (p->text_emitted)
115     fprintf(stderr, "[t] ");
116 }
117
118 /*
119  *  dump_stack - debugging function only.
120  */
121
122 void html_text::dump_stack (void)
123 {
124   if (debugStack) {
125     tag_definition *p = stackptr;
126
127     while (p != NULL) {
128       dump_stack_element(p);
129       p = p->next;
130     }
131   }
132   fprintf(stderr, "\n");
133   fflush(stderr);
134 }
135 #else
136 void html_text::dump_stack (void) {}
137 #endif
138
139
140 /*
141  *  end_tag - shuts down the tag.
142  */
143
144 void html_text::end_tag (tag_definition *t)
145 {
146   switch (t->type) {
147
148   case I_TAG:      out->put_string("</i>"); break;
149   case B_TAG:      out->put_string("</b>"); break;
150   case P_TAG:      if (t->indent == NULL) {
151                      out->put_string("</p>");
152                    } else {
153                      delete t->indent;
154                      t->indent = NULL;
155                      out->put_string("</p>");
156                    }
157                    out->enable_newlines(FALSE);
158                    blank_para = TRUE; break;
159   case SUB_TAG:    out->put_string("</sub>"); break;
160   case SUP_TAG:    out->put_string("</sup>"); break;
161   case TT_TAG:     out->put_string("</tt>"); break;
162   case PRE_TAG:    out->put_string("</pre>"); out->enable_newlines(TRUE);
163                    blank_para = TRUE;
164                    if (t->indent != NULL)
165                      delete t->indent;
166                    t->indent = NULL;
167                    break;
168   case SMALL_TAG:  if (! is_in_pre ())
169                      out->put_string("</small>");
170                    break;
171   case BIG_TAG:    if (! is_in_pre ())
172                      out->put_string("</big>");
173                    break;
174   case COLOR_TAG:  if (! is_in_pre ())
175                      out->put_string("</font>");
176                    break;
177
178   default:
179     error("unrecognised tag");
180   }
181 }
182
183 /*
184  *  issue_tag - writes out an html tag with argument.
185  *              space == 0 if no space is requested
186  *              space == 1 if a space is requested
187  *              space == 2 if tag should not have a space style
188  */
189
190 void html_text::issue_tag (const char *tagname, const char *arg,
191                            int space)
192 {
193   if ((arg == 0) || (strlen(arg) == 0))
194     out->put_string(tagname);
195   else {
196     out->put_string(tagname);
197     out->put_string(" ");
198     out->put_string(arg);
199   }
200   if (space == TRUE) {
201     out->put_string(" style=\"margin-top: ");
202     out->put_string(STYLE_VERTICAL_SPACE);
203     out->put_string("\"");
204   }
205 #if 0
206   if (space == TRUE || space == FALSE)
207     out->put_string(" valign=\"top\"");
208 #endif
209   out->put_string(">");
210 }
211
212 /*
213  *  issue_color_begin - writes out an html color tag.
214  */
215
216 void html_text::issue_color_begin (color *c)
217 {
218   unsigned int r, g, b;
219   char buf[6+1];
220
221   out->put_string("<font color=\"#");
222   if (c->is_default())
223     sprintf(buf, "000000");
224   else {
225     c->get_rgb(&r, &g, &b);
226     // we have to scale 0..0xFFFF to 0..0xFF
227     sprintf(buf, "%.2X%.2X%.2X", r/0x101, g/0x101, b/0x101);
228   }
229   out->put_string(buf);
230   out->put_string("\">");
231 }
232
233 /*
234  *  start_tag - starts a tag.
235  */
236
237 void html_text::start_tag (tag_definition *t)
238 {
239   switch (t->type) {
240
241   case I_TAG:      issue_tag("<i", (char *)t->arg1); break;
242   case B_TAG:      issue_tag("<b", (char *)t->arg1); break;
243   case P_TAG:      if (t->indent != NULL) {
244                      out->nl();
245 #if defined(DEBUGGING)
246                      out->simple_comment("INDENTATION");
247 #endif
248                      out->put_string("\n<p");
249                      t->indent->begin(start_space);
250                      issue_tag("", (char *)t->arg1);
251                    } else {
252                      out->nl();
253                      issue_tag("\n<p", (char *)t->arg1, start_space);
254                    }
255
256                    out->enable_newlines(TRUE); break;
257   case SUB_TAG:    issue_tag("<sub", (char *)t->arg1); break;
258   case SUP_TAG:    issue_tag("<sup", (char *)t->arg1); break;
259   case TT_TAG:     issue_tag("<tt", (char *)t->arg1); break;
260   case PRE_TAG:    out->enable_newlines(TRUE);
261                    out->nl(); out->put_string("<pre");
262                    if (t->indent == NULL)
263                      issue_tag("", (char *)t->arg1, start_space);
264                    else {
265                      t->indent->begin(start_space);
266                      issue_tag("", (char *)t->arg1);
267                    }
268                    out->enable_newlines(FALSE); break;
269   case SMALL_TAG:  if (! is_in_pre ())
270                      issue_tag("<small", (char *)t->arg1);
271                    break;
272   case BIG_TAG:    if (! is_in_pre ())
273                      issue_tag("<big", (char *)t->arg1);
274                    break;
275   case BREAK_TAG:  break;
276   case COLOR_TAG:  if (! is_in_pre ())
277                      issue_color_begin(&t->col);
278                    break;
279
280   default:
281     error("unrecognised tag");
282   }
283 }
284
285 /*
286  *  flush_text - flushes html tags which are outstanding on the html stack.
287  */
288
289 void html_text::flush_text (void)
290 {
291   int notext=TRUE;
292   tag_definition *p=stackptr;
293
294   while (stackptr != 0) {
295     notext = (notext && (! stackptr->text_emitted));
296     if (! notext) {
297       end_tag(stackptr);
298     }
299     p = stackptr;
300     stackptr = stackptr->next;
301     delete p;
302   }
303   lastptr = NULL;
304 }
305
306 /*
307  *  is_present - returns TRUE if tag is already present on the stack.
308  */
309
310 int html_text::is_present (HTML_TAG t)
311 {
312   tag_definition *p=stackptr;
313
314   while (p != NULL) {
315     if (t == p->type)
316       return TRUE;
317     p = p->next;
318   }
319   return FALSE;
320 }
321
322 /*
323  *  uses_indent - returns TRUE if the current paragraph is using a
324  *                html table to effect an indent.
325  */
326
327 int html_text::uses_indent (void)
328 {
329   tag_definition *p = stackptr;
330
331   while (p != NULL) {
332     if (p->indent != NULL)
333       return TRUE;
334     p = p->next;
335   }
336   return FALSE;
337 }
338
339 extern void stop();
340
341 /*
342  *  do_push - places, tag_definition, p, onto the stack
343  */
344
345 void html_text::do_push (tag_definition *p)
346 {
347   HTML_TAG t = p->type;
348
349 #if defined(DEBUGGING)
350   if (t == PRE_TAG)
351     stop();
352   debugStack = TRUE;
353   fprintf(stderr, "\nentering do_push (");
354   dump_stack_element(p);
355   fprintf(stderr, ")\n");
356   dump_stack();
357   fprintf(stderr, ")\n");
358   fflush(stderr);
359 #endif
360
361   /*
362    *  if t is a P_TAG or PRE_TAG make sure it goes on the end of the stack.
363    */
364
365   if (((t == P_TAG) || (t == PRE_TAG)) && (lastptr != NULL)) {
366     /*
367      *  store, p, at the end
368      */
369     lastptr->next = p;
370     lastptr       = p;
371     p->next       = NULL;
372   } else {
373     p->next       = stackptr;
374     if (stackptr == NULL)
375       lastptr = p;
376     stackptr      = p;
377   }
378
379 #if defined(DEBUGGING)
380   dump_stack();
381   fprintf(stderr, "exiting do_push\n");
382 #endif
383 }
384
385 /*
386  *  push_para - adds a new entry onto the html paragraph stack.
387  */
388
389 void html_text::push_para (HTML_TAG t, void *arg, html_indent *in)
390 {
391   tag_definition *p= new tag_definition;
392
393   p->type         = t;
394   p->arg1         = arg;
395   p->text_emitted = FALSE;
396   p->indent       = in;
397
398   if (t == PRE_TAG && is_present(PRE_TAG))
399     fatal("cannot have multiple PRE_TAGs");
400
401   do_push(p);
402 }
403
404 void html_text::push_para (HTML_TAG t)
405 {
406   push_para(t, (void *)"", NULL);
407 }
408
409 void html_text::push_para (color *c)
410 {
411   tag_definition *p = new tag_definition;
412
413   p->type         = COLOR_TAG;
414   p->arg1         = NULL;
415   p->col          = *c;
416   p->text_emitted = FALSE;
417   p->indent       = NULL;
418
419   do_push(p);
420 }
421
422 /*
423  *  do_italic - changes to italic
424  */
425
426 void html_text::do_italic (void)
427 {
428   if (! is_present(I_TAG))
429     push_para(I_TAG);
430 }
431
432 /*
433  *  do_bold - changes to bold.
434  */
435
436 void html_text::do_bold (void)
437 {
438   if (! is_present(B_TAG))
439     push_para(B_TAG);
440 }
441
442 /*
443  *  do_tt - changes to teletype.
444  */
445
446 void html_text::do_tt (void)
447 {
448   if ((! is_present(TT_TAG)) && (! is_present(PRE_TAG)))
449     push_para(TT_TAG);
450 }
451
452 /*
453  *  do_pre - changes to preformated text.
454  */
455
456 void html_text::do_pre (void)
457 {
458   done_tt();
459   if (is_present(P_TAG)) {
460     html_indent *i = remove_indent(P_TAG);
461     int space = retrieve_para_space();
462     (void)done_para();
463     if (! is_present(PRE_TAG))
464       push_para(PRE_TAG, NULL, i);
465     start_space = space;
466   } else if (! is_present(PRE_TAG))
467     push_para(PRE_TAG, NULL, NULL);
468   dump_stack();
469 }
470
471 /*
472  *  is_in_pre - returns TRUE if we are currently within a preformatted
473  *              <pre> block.
474  */
475
476 int html_text::is_in_pre (void)
477 {
478   return is_present(PRE_TAG);
479 }
480
481 /*
482  *  do_color - initiates a new color tag.
483  */
484
485 void html_text::do_color (color *c)
486 {
487   shutdown(COLOR_TAG);   // shutdown a previous color tag, if present
488   push_para(c);
489 }
490
491 /*
492  *  done_color - shutdown an outstanding color tag, if it exists.
493  */
494
495 void html_text::done_color (void)
496 {
497   shutdown(COLOR_TAG);
498 }
499
500 /*
501  *  shutdown - shuts down an html tag.
502  */
503
504 char *html_text::shutdown (HTML_TAG t)
505 {
506   char *arg=NULL;
507
508   if (is_present(t)) {
509     tag_definition *p    =stackptr;
510     tag_definition *temp =NULL;
511     int notext           =TRUE;
512     
513     dump_stack();
514     while ((stackptr != NULL) && (stackptr->type != t)) {
515       notext = (notext && (! stackptr->text_emitted));
516       if (! notext) {
517         end_tag(stackptr);
518       }
519
520       /*
521        *  pop tag
522        */
523       p        = stackptr;
524       stackptr = stackptr->next;
525       if (stackptr == NULL)
526         lastptr = NULL;
527     
528       /*
529        *  push tag onto temp stack
530        */
531       p->next = temp;
532       temp    = p;
533     }
534
535     /*
536      *  and examine stackptr
537      */
538     if ((stackptr != NULL) && (stackptr->type == t)) {
539       if (stackptr->text_emitted) {
540         end_tag(stackptr);
541       }
542       if (t == P_TAG) {
543         arg = (char *)stackptr->arg1;
544       }
545       p        = stackptr;
546       stackptr = stackptr->next;
547       if (stackptr == NULL)
548         lastptr = NULL;
549       if (p->indent != NULL)
550         delete p->indent;
551       delete p;
552     }
553
554     /*
555      *  and restore unaffected tags
556      */
557     while (temp != NULL) {
558       if (temp->type == COLOR_TAG)
559         push_para(&temp->col);
560       else
561         push_para(temp->type, temp->arg1, temp->indent);
562       p    = temp;
563       temp = temp->next;
564       delete p;
565     }
566   }
567   return arg;
568 }
569
570 /*
571  *  done_bold - shuts downs a bold tag.
572  */
573
574 void html_text::done_bold (void)
575 {
576   shutdown(B_TAG);
577 }
578
579 /*
580  *  done_italic - shuts downs an italic tag.
581  */
582
583 void html_text::done_italic (void)
584 {
585   shutdown(I_TAG);
586 }
587
588 /*
589  *  done_sup - shuts downs a sup tag.
590  */
591
592 void html_text::done_sup (void)
593 {
594   shutdown(SUP_TAG);
595 }
596
597 /*
598  *  done_sub - shuts downs a sub tag.
599  */
600
601 void html_text::done_sub (void)
602 {
603   shutdown(SUB_TAG);
604 }
605
606 /*
607  *  done_tt - shuts downs a tt tag.
608  */
609
610 void html_text::done_tt (void)
611 {
612   shutdown(TT_TAG);
613 }
614
615 /*
616  *  done_pre - shuts downs a pre tag.
617  */
618
619 void html_text::done_pre (void)
620 {
621   shutdown(PRE_TAG);
622 }
623
624 /*
625  *  done_small - shuts downs a small tag.
626  */
627
628 void html_text::done_small (void)
629 {
630   shutdown(SMALL_TAG);
631 }
632
633 /*
634  *  done_big - shuts downs a big tag.
635  */
636
637 void html_text::done_big (void)
638 {
639   shutdown(BIG_TAG);
640 }
641
642 /*
643  *  check_emit_text - ensures that all previous tags have been emitted (in order)
644  *                    before the text is written.
645  */
646
647 void html_text::check_emit_text (tag_definition *t)
648 {
649   if ((t != NULL) && (! t->text_emitted)) {
650     check_emit_text(t->next);
651     t->text_emitted = TRUE;
652     start_tag(t);
653   }
654 }
655
656 /*
657  *  do_emittext - tells the class that text was written during the current tag.
658  */
659
660 void html_text::do_emittext (const char *s, int length)
661 {
662   if ((! is_present(P_TAG)) && (! is_present(PRE_TAG)))
663     do_para("", FALSE);
664
665   if (is_present(BREAK_TAG)) {
666     int text = remove_break();
667     check_emit_text(stackptr);
668     if (text) {
669       if (is_present(PRE_TAG))
670         out->nl();
671       else if (dialect == xhtml)
672         out->put_string("<br/>").nl();
673       else
674         out->put_string("<br>").nl();
675     }
676   } else
677     check_emit_text(stackptr);
678
679   out->put_string(s, length);
680   space_emitted = FALSE;
681   blank_para = FALSE;
682 }
683
684 /*
685  *  do_para - starts a new paragraph
686  */
687
688 void html_text::do_para (const char *arg, html_indent *in, int space)
689 {
690   if (! is_present(P_TAG)) {
691     if (is_present(PRE_TAG)) {
692       html_indent *i = remove_indent(PRE_TAG);
693       done_pre();    
694       if ((arg == NULL || (strcmp(arg, "") == 0)) &&
695           (i == in || in == NULL))
696         in = i;
697       else
698         delete i;
699     }
700     remove_sub_sup();
701     push_para(P_TAG, (void *)arg, in);
702     start_space = space;
703   }
704 }
705
706 void html_text::do_para (const char *arg, int space)
707 {
708   do_para(arg, NULL, space);
709 }
710
711 void html_text::do_para (simple_output *op, const char *arg1,
712                          int indentation_value, int page_offset,
713                          int line_length, int space)
714 {
715   html_indent *ind;
716
717   if (indentation_value == 0)
718     ind = NULL;
719   else
720     ind = new html_indent(op, indentation_value, page_offset, line_length);
721   do_para(arg1, ind, space);
722 }
723
724 /*
725  *  done_para - shuts down a paragraph tag.
726  */
727
728 char *html_text::done_para (void)
729 {
730   char *result;
731   space_emitted = TRUE;
732   result = shutdown(P_TAG);
733   start_space = FALSE;
734   return result;
735 }
736
737 /*
738  *  remove_indent - returns the indent associated with, tag.
739  *                  The indent associated with tag is set to NULL.
740  */
741
742 html_indent *html_text::remove_indent (HTML_TAG tag)
743 {
744   tag_definition *p=stackptr;
745
746   while (p != NULL) {
747     if (tag == p->type) {
748       html_indent *i = p->indent;
749       p->indent = NULL;
750       return i;
751     }
752     p = p->next;
753   }
754   return NULL;
755 }
756
757 /*
758  *  remove_para_space - removes the leading space to a paragraph
759  *                      (effectively this trims off a leading `.sp' tag).
760  */
761
762 void html_text::remove_para_space (void)
763 {
764   start_space = FALSE;
765 }
766
767 /*
768  *  do_space - issues an end of paragraph
769  */
770
771 void html_text::do_space (void)
772 {
773   if (is_in_pre()) {
774     do_emittext("", 0);
775     out->force_nl();
776     space_emitted = TRUE;
777   } else {
778     html_indent *i = remove_indent(P_TAG);
779
780     do_para(done_para(), i, TRUE);
781     space_emitted = TRUE;
782   }
783 }
784
785 /*
786  *  do_break - issue a break tag.
787  */
788
789 void html_text::do_break (void)
790 {
791   if (! is_present(PRE_TAG))
792     if (emitted_text())
793       if (! is_present(BREAK_TAG))
794         push_para(BREAK_TAG);
795
796   space_emitted = TRUE;
797 }
798
799 /*
800  *  do_newline - issue a newline providing that we are inside a <pre> tag.
801  */
802
803 void html_text::do_newline (void)
804 {
805   if (is_present(PRE_TAG)) {
806     do_emittext("\n", 1);
807     space_emitted = TRUE;
808   }
809 }
810
811 /*
812  *  emitted_text - returns FALSE if white space has just been written.
813  */
814
815 int html_text::emitted_text (void)
816 {
817   return !space_emitted;
818 }
819
820 /*
821  *  ever_emitted_text - returns TRUE if we have ever emitted text in this
822  *                      paragraph.
823  */
824
825 int html_text::ever_emitted_text (void)
826 {
827   return !blank_para;
828 }
829
830 /*
831  *  starts_with_space - returns TRUE if we started this paragraph with a .sp
832  */
833
834 int html_text::starts_with_space (void)
835 {
836   return start_space;
837 }
838
839 /*
840  *  retrieve_para_space - returns TRUE, if the paragraph starts with
841  *                        a space and text has not yet been emitted.
842  *                        If TRUE is returned, then the, start_space,
843  *                        variable is set to FALSE.
844  */
845
846 int html_text::retrieve_para_space (void)
847 {
848   if (start_space && blank_para) {
849     start_space = FALSE;
850     return TRUE;
851   }
852   else
853     return FALSE;
854 }
855
856 /*
857  *  emit_space - writes a space providing that text was written beforehand.
858  */
859
860 void html_text::emit_space (void)
861 {
862   if (is_present(PRE_TAG))
863     do_emittext(" ", 1);
864   else
865     out->space_or_newline();
866
867   space_emitted = TRUE;
868 }
869
870 /*
871  *  remove_def - removes a definition, t, from the stack.
872  */
873
874 void html_text::remove_def (tag_definition *t)
875 {
876   tag_definition *p    = stackptr;
877   tag_definition *l    = 0;
878   tag_definition *q    = 0;
879     
880   while ((p != 0) && (p != t)) {
881     l = p;
882     p = p->next;
883   }
884   if ((p != 0) && (p == t)) {
885     if (p == stackptr) {
886       stackptr = stackptr->next;
887       if (stackptr == NULL)
888         lastptr = NULL;
889       q = stackptr;
890     } else if (l == 0) {
891       error("stack list pointers are wrong");
892     } else {
893       l->next = p->next;
894       q = p->next;
895       if (l->next == NULL)
896         lastptr = l;
897     }
898     delete p;
899   }
900 }
901
902 /*
903  *  remove_tag - removes a tag from the stack.
904  */
905
906 void html_text::remove_tag (HTML_TAG tag)
907 {
908   tag_definition *p = stackptr;
909     
910   while ((p != 0) && (p->type != tag)) {
911     p = p->next;
912   }
913   if ((p != 0) && (p->type == tag))
914     remove_def(p);
915 }
916
917 /*
918  *  remove_sub_sup - removes a sub or sup tag, should either exist
919  *                   on the stack.
920  */
921
922 void html_text::remove_sub_sup (void)
923 {
924   if (is_present(SUB_TAG)) {
925     remove_tag(SUB_TAG);
926   }
927   if (is_present(SUP_TAG)) {
928     remove_tag(SUP_TAG);
929   }
930   if (is_present(PRE_TAG)) {
931     remove_tag(PRE_TAG);
932   }
933 }
934
935 /*
936  *  remove_break - break tags are not balanced thus remove it once it has been emitted.
937  *                 It returns TRUE if text was emitted before the <br> was issued.
938  */
939
940 int html_text::remove_break (void)
941 {
942   tag_definition *p    = stackptr;
943   tag_definition *l    = 0;
944   tag_definition *q    = 0;
945
946   while ((p != 0) && (p->type != BREAK_TAG)) {
947     l = p;
948     p = p->next;
949   }
950   if ((p != 0) && (p->type == BREAK_TAG)) {
951     if (p == stackptr) {
952       stackptr = stackptr->next;
953       if (stackptr == NULL)
954         lastptr = NULL;
955       q = stackptr;
956     } else if (l == 0)
957       error("stack list pointers are wrong");
958     else {
959       l->next = p->next;
960       q = p->next;
961       if (l->next == NULL)
962         lastptr = l;
963     }
964     delete p;
965   }
966   /*
967    *  now determine whether text was issued before <br>
968    */
969   while (q != 0) {
970     if (q->text_emitted)
971       return TRUE;
972     else
973       q = q->next;
974   }
975   return FALSE;
976 }
977
978 /*
979  *  remove_para_align - removes a paragraph which has a text
980  *                      argument. If the paragraph has no text
981  *                      argument then it is left alone.
982  */
983
984 void html_text::remove_para_align (void)
985 {
986   if (is_present(P_TAG)) {
987     tag_definition *p=stackptr;
988
989     while (p != NULL) {
990       if (p->type == P_TAG && p->arg1 != NULL) {
991         html_indent *i = remove_indent(P_TAG);
992         int          space = retrieve_para_space();
993         done_para();
994         do_para("", i, space);
995         return;
996       }
997       p = p->next;
998     }
999   }
1000 }
1001
1002 /*
1003  *  get_alignment - returns the alignment for the paragraph.
1004  *                  If no alignment was given then we return "".
1005  */
1006
1007 char *html_text::get_alignment (void)
1008 {
1009   if (is_present(P_TAG)) {
1010     tag_definition *p=stackptr;
1011
1012     while (p != NULL) {
1013       if (p->type == P_TAG && p->arg1 != NULL)
1014         return (char *)p->arg1;
1015       p = p->next;
1016     }
1017   }
1018   return (char *)"";
1019 }
1020
1021 /*
1022  *  do_small - potentially inserts a <small> tag into the html stream.
1023  *             However we check for a <big> tag, if present then we terminate it.
1024  *             Otherwise a <small> tag is inserted.
1025  */
1026
1027 void html_text::do_small (void)
1028 {
1029   if (is_present(BIG_TAG))
1030     done_big();
1031   else
1032     push_para(SMALL_TAG);
1033 }
1034
1035 /*
1036  *  do_big - is the mirror image of do_small.
1037  */
1038
1039 void html_text::do_big (void)
1040 {
1041   if (is_present(SMALL_TAG))
1042     done_small();
1043   else
1044     push_para(BIG_TAG);
1045 }
1046
1047 /*
1048  *  do_sup - save a superscript tag on the stack of tags.
1049  */
1050
1051 void html_text::do_sup (void)
1052 {
1053   push_para(SUP_TAG);
1054 }
1055
1056 /*
1057  *  do_sub - save a subscript tag on the stack of tags.
1058  */
1059
1060 void html_text::do_sub (void)
1061 {
1062   push_para(SUB_TAG);
1063 }