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