summaryrefslogtreecommitdiff
path: root/gnulib-local/lib/html-ostream.oo.c
diff options
context:
space:
mode:
Diffstat (limited to 'gnulib-local/lib/html-ostream.oo.c')
-rw-r--r--gnulib-local/lib/html-ostream.oo.c277
1 files changed, 277 insertions, 0 deletions
diff --git a/gnulib-local/lib/html-ostream.oo.c b/gnulib-local/lib/html-ostream.oo.c
new file mode 100644
index 0000000..bf5c130
--- /dev/null
+++ b/gnulib-local/lib/html-ostream.oo.c
@@ -0,0 +1,277 @@
+/* Output stream that produces HTML output.
+ Copyright (C) 2006-2009 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+/* Specification. */
+#include "html-ostream.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "gl_xlist.h"
+#include "gl_array_list.h"
+#include "unistr.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;
+
+ 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;
+ ucs4_t 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);
+
+ if (uc == '\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;
+ }
+ else
+ {
+ emit_pending_spans (stream, true);
+
+ switch (uc)
+ {
+ case '"':
+ ostream_write_str (stream->destination, "&quot;");
+ break;
+ case '&':
+ ostream_write_str (stream->destination, "&amp;");
+ break;
+ case '<':
+ ostream_write_str (stream->destination, "&lt;");
+ break;
+ case '>':
+ /* Needed to avoid "]]>" in the output. */
+ ostream_write_str (stream->destination, "&gt;");
+ break;
+ case ' ':
+ /* Needed because HTML viewers merge adjacent spaces
+ and drop spaces adjacent to <br> and similar. */
+ ostream_write_str (stream->destination, "&nbsp;");
+ 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;", (int) 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. */
+
+static 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++;
+}
+
+static 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, NULL, true);
+ stream->curr_class_stack_size = 0;
+ stream->last_class_stack_size = 0;
+ stream->buflen = 0;
+
+ return stream;
+}