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