--- /dev/null
+/* Output stream that produces HTML output.
+ Copyright (C) 2006 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2006.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#include <config.h>
+
+/* Specification. */
+#include "html-ostream.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "gl_list.h"
+#include "gl_array_list.h"
+#include "utf8-ucs4.h"
+#include "xalloc.h"
+
+struct html_ostream : struct ostream
+{
+fields:
+ /* The destination stream. */
+ ostream_t destination;
+ /* The stack of active CSS classes. */
+ gl_list_t /* <char *> */ class_stack;
+ /* Current and last size of the active portion of this stack. Always
+ size(class_stack) == max(curr_class_stack_size,last_class_stack_size). */
+ size_t curr_class_stack_size;
+ size_t last_class_stack_size;
+ /* Last few bytes that could not yet be converted. */
+ #define BUFSIZE 6
+ char buf[BUFSIZE];
+ size_t buflen;
+};
+
+/* Implementation of ostream_t methods. */
+
+static void
+emit_pending_spans (html_ostream_t stream, bool shrink_stack)
+{
+ if (stream->curr_class_stack_size > stream->last_class_stack_size)
+ {
+ size_t i;
+
+ for (i = stream->last_class_stack_size; i < stream->curr_class_stack_size; i++)
+ {
+ char *classname = (char *) gl_list_get_at (stream->class_stack, i);
+
+ ostream_write_str (stream->destination, "<span class=\"");
+ ostream_write_str (stream->destination, classname);
+ ostream_write_str (stream->destination, ">");
+ }
+ stream->last_class_stack_size = stream->curr_class_stack_size;
+ }
+ else if (stream->curr_class_stack_size < stream->last_class_stack_size)
+ {
+ size_t i = stream->last_class_stack_size;
+
+ while (i > stream->curr_class_stack_size)
+ {
+ char *classname;
+
+ --i;
+ classname = (char *) gl_list_get_at (stream->class_stack, i);
+ ostream_write_str (stream->destination, "</span>");
+ if (shrink_stack)
+ {
+ gl_list_remove_at (stream->class_stack, i);
+ free (classname);
+ }
+ }
+ stream->last_class_stack_size = stream->curr_class_stack_size;
+ }
+}
+
+static void
+html_ostream::write_mem (html_ostream_t stream, const void *data, size_t len)
+{
+ if (len > 0)
+ {
+ #define BUFFERSIZE 2048
+ char inbuffer[BUFFERSIZE];
+ size_t inbufcount;
+
+ emit_pending_spans (stream, true);
+
+ inbufcount = stream->buflen;
+ if (inbufcount > 0)
+ memcpy (inbuffer, stream->buf, inbufcount);
+ for (;;)
+ {
+ /* At this point, inbuffer[0..inbufcount-1] is filled. */
+ {
+ /* Combine the previous rest with a chunk of new input. */
+ size_t n =
+ (len <= BUFFERSIZE - inbufcount ? len : BUFFERSIZE - inbufcount);
+
+ if (n > 0)
+ {
+ memcpy (inbuffer + inbufcount, data, n);
+ data = (char *) data + n;
+ inbufcount += n;
+ len -= n;
+ }
+ }
+ {
+ /* Handle complete UTF-8 characters. */
+ const char *inptr = inbuffer;
+ size_t insize = inbufcount;
+
+ while (insize > 0)
+ {
+ unsigned char c0;
+ unsigned int uc;
+ int nbytes;
+
+ c0 = ((const unsigned char *) inptr)[0];
+ if (insize < (c0 < 0xc0 ? 1 : c0 < 0xe0 ? 2 : c0 < 0xf0 ? 3 :
+ c0 < 0xf8 ? 4 : c0 < 0xfc ? 5 : 6))
+ break;
+
+ nbytes = u8_mbtouc (&uc, (const unsigned char *) inptr, insize);
+
+ switch (uc)
+ {
+ case '"':
+ ostream_write_str (stream->destination, """);
+ break;
+ case '&':
+ ostream_write_str (stream->destination, "&");
+ break;
+ case '<':
+ ostream_write_str (stream->destination, "<");
+ break;
+ case '>':
+ /* Needed to avoid "]]>" in the output. */
+ ostream_write_str (stream->destination, ">");
+ break;
+ case ' ':
+ /* Needed because HTML viewers merge adjacent spaces and
+ drop spaces adjacent to <br> and similar. */
+ ostream_write_str (stream->destination, " ");
+ break;
+ case '\n':
+ {
+ size_t prev_class_stack_size = stream->curr_class_stack_size;
+ stream->curr_class_stack_size = 0;
+ emit_pending_spans (stream, false);
+ ostream_write_str (stream->destination, "<br/>");
+ stream->curr_class_stack_size = prev_class_stack_size;
+ }
+ break;
+ default:
+ if (uc >= 0x20 && uc < 0x7F)
+ {
+ /* Output ASCII characters as such. */
+ char bytes[1];
+ bytes[0] = uc;
+ ostream_write_mem (stream->destination, bytes, 1);
+ }
+ else
+ {
+ /* Output non-ASCII characters in #&nnn; notation. */
+ char bytes[32];
+ sprintf (bytes, "&#%d;", uc);
+ ostream_write_str (stream->destination, bytes);
+ }
+ break;
+ }
+
+ inptr += nbytes;
+ insize -= nbytes;
+ }
+ /* Put back the unconverted part. */
+ if (insize > BUFSIZE)
+ abort ();
+ if (len == 0)
+ {
+ if (insize > 0)
+ memcpy (stream->buf, inptr, insize);
+ stream->buflen = insize;
+ break;
+ }
+ if (insize > 0)
+ memmove (inbuffer, inptr, insize);
+ inbufcount = insize;
+ }
+ }
+ #undef BUFFERSIZE
+ }
+}
+
+static void
+html_ostream::flush (html_ostream_t stream)
+{
+ /* There's nothing to do here, since stream->buf[] contains only a few
+ bytes that don't correspond to a character, and it's not worth closing
+ the open spans. */
+}
+
+static void
+html_ostream::free (html_ostream_t stream)
+{
+ stream->curr_class_stack_size = 0;
+ emit_pending_spans (stream, true);
+ gl_list_free (stream->class_stack);
+ free (stream);
+}
+
+/* Implementation of html_ostream_t methods. */
+
+void
+html_ostream::begin_span (html_ostream_t stream, const char *classname)
+{
+ if (stream->last_class_stack_size > stream->curr_class_stack_size
+ && strcmp ((char *) gl_list_get_at (stream->class_stack,
+ stream->curr_class_stack_size),
+ classname) != 0)
+ emit_pending_spans (stream, true);
+ /* Now either
+ last_class_stack_size <= curr_class_stack_size
+ - in this case we have to append the given CLASSNAME -
+ or
+ last_class_stack_size > curr_class_stack_size
+ && class_stack[curr_class_stack_size] == CLASSNAME
+ - in this case we only need to increment curr_class_stack_size. */
+ if (stream->last_class_stack_size <= stream->curr_class_stack_size)
+ gl_list_add_at (stream->class_stack, stream->curr_class_stack_size,
+ xstrdup (classname));
+ stream->curr_class_stack_size++;
+}
+
+void
+html_ostream::end_span (html_ostream_t stream, const char *classname)
+{
+ if (!(stream->curr_class_stack_size > 0
+ && strcmp ((char *) gl_list_get_at (stream->class_stack,
+ stream->curr_class_stack_size - 1),
+ classname) == 0))
+ /* Improperly nested begin_span/end_span calls. */
+ abort ();
+ stream->curr_class_stack_size--;
+}
+
+/* Constructor. */
+
+html_ostream_t
+html_ostream_create (ostream_t destination)
+{
+ html_ostream_t stream = XMALLOC (struct html_ostream_representation);
+
+ stream->base.vtable = &html_ostream_vtable;
+ stream->destination = destination;
+ stream->class_stack = gl_list_create_empty (GL_ARRAY_LIST, NULL, NULL, true);
+ stream->curr_class_stack_size = 0;
+ stream->last_class_stack_size = 0;
+ stream->buflen = 0;
+
+ return stream;
+}
--- /dev/null
+/* Output stream that produces HTML output.
+ Copyright (C) 2006 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2006.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#ifndef _HTML_OSTREAM_H
+#define _HTML_OSTREAM_H
+
+#include "ostream.h"
+
+
+struct html_ostream : struct ostream
+{
+methods:
+
+ /* Start a <span class="CLASSNAME"> element. The CLASSNAME is the name
+ of a CSS class. It can be chosen arbitrarily and customized through
+ an inline or external CSS. */
+ void begin_span (html_ostream_t stream, const char *classname);
+
+ /* End a <span class="CLASSNAME"> element.
+ The begin_span / end_span calls must match properly. */
+ void end_span (html_ostream_t stream, const char *classname);
+};
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Create an output stream that takes input in the UTF-8 encoding and
+ writes it in HTML form on DESTINATION.
+ This stream produces a sequence of lines. The caller is responsible
+ for opening the <body><html> elements before and for closing them after
+ the use of this stream.
+ Note that the resulting stream must be closed before DESTINATION can be
+ closed. */
+extern html_ostream_t html_ostream_create (ostream_t destination);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _HTML_OSTREAM_H */