diff options
Diffstat (limited to 'gnulib-local/lib/html-ostream.oo.c')
-rw-r--r-- | gnulib-local/lib/html-ostream.oo.c | 277 |
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, """); + 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; + 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; +} |