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