1 /* Output stream that produces HTML output.
2 Copyright (C) 2006-2009 Free Software Foundation, Inc.
3 Written by Bruno Haible <bruno@clisp.org>, 2006.
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>. */
21 #include "html-ostream.h"
28 #include "gl_array_list.h"
32 struct html_ostream : struct ostream
35 /* The destination stream. */
36 ostream_t destination;
37 /* The stack of active CSS classes. */
38 gl_list_t /* <char *> */ class_stack;
39 /* Current and last size of the active portion of this stack. Always
40 size(class_stack) == max(curr_class_stack_size,last_class_stack_size). */
41 size_t curr_class_stack_size;
42 size_t last_class_stack_size;
43 /* Last few bytes that could not yet be converted. */
49 /* Implementation of ostream_t methods. */
52 emit_pending_spans (html_ostream_t stream, bool shrink_stack)
54 if (stream->curr_class_stack_size > stream->last_class_stack_size)
58 for (i = stream->last_class_stack_size; i < stream->curr_class_stack_size; i++)
60 char *classname = (char *) gl_list_get_at (stream->class_stack, i);
62 ostream_write_str (stream->destination, "<span class=\"");
63 ostream_write_str (stream->destination, classname);
64 ostream_write_str (stream->destination, "\">");
66 stream->last_class_stack_size = stream->curr_class_stack_size;
68 else if (stream->curr_class_stack_size < stream->last_class_stack_size)
70 size_t i = stream->last_class_stack_size;
72 while (i > stream->curr_class_stack_size)
77 classname = (char *) gl_list_get_at (stream->class_stack, i);
78 ostream_write_str (stream->destination, "</span>");
81 gl_list_remove_at (stream->class_stack, i);
85 stream->last_class_stack_size = stream->curr_class_stack_size;
90 html_ostream::write_mem (html_ostream_t stream, const void *data, size_t len)
94 #define BUFFERSIZE 2048
95 char inbuffer[BUFFERSIZE];
98 inbufcount = stream->buflen;
100 memcpy (inbuffer, stream->buf, inbufcount);
103 /* At this point, inbuffer[0..inbufcount-1] is filled. */
105 /* Combine the previous rest with a chunk of new input. */
107 (len <= BUFFERSIZE - inbufcount ? len : BUFFERSIZE - inbufcount);
111 memcpy (inbuffer + inbufcount, data, n);
112 data = (char *) data + n;
118 /* Handle complete UTF-8 characters. */
119 const char *inptr = inbuffer;
120 size_t insize = inbufcount;
128 c0 = ((const unsigned char *) inptr)[0];
129 if (insize < (c0 < 0xc0 ? 1 : c0 < 0xe0 ? 2 : c0 < 0xf0 ? 3 :
130 c0 < 0xf8 ? 4 : c0 < 0xfc ? 5 : 6))
133 nbytes = u8_mbtouc (&uc, (const unsigned char *) inptr, insize);
137 size_t prev_class_stack_size = stream->curr_class_stack_size;
138 stream->curr_class_stack_size = 0;
139 emit_pending_spans (stream, false);
140 ostream_write_str (stream->destination, "<br/>");
141 stream->curr_class_stack_size = prev_class_stack_size;
145 emit_pending_spans (stream, true);
150 ostream_write_str (stream->destination, """);
153 ostream_write_str (stream->destination, "&");
156 ostream_write_str (stream->destination, "<");
159 /* Needed to avoid "]]>" in the output. */
160 ostream_write_str (stream->destination, ">");
163 /* Needed because HTML viewers merge adjacent spaces
164 and drop spaces adjacent to <br> and similar. */
165 ostream_write_str (stream->destination, " ");
168 if (uc >= 0x20 && uc < 0x7F)
170 /* Output ASCII characters as such. */
173 ostream_write_mem (stream->destination, bytes, 1);
177 /* Output non-ASCII characters in #&nnn;
180 sprintf (bytes, "&#%d;", (int) uc);
181 ostream_write_str (stream->destination, bytes);
190 /* Put back the unconverted part. */
191 if (insize > BUFSIZE)
196 memcpy (stream->buf, inptr, insize);
197 stream->buflen = insize;
201 memmove (inbuffer, inptr, insize);
210 html_ostream::flush (html_ostream_t stream)
212 /* There's nothing to do here, since stream->buf[] contains only a few
213 bytes that don't correspond to a character, and it's not worth closing
218 html_ostream::free (html_ostream_t stream)
220 stream->curr_class_stack_size = 0;
221 emit_pending_spans (stream, true);
222 gl_list_free (stream->class_stack);
226 /* Implementation of html_ostream_t methods. */
229 html_ostream::begin_span (html_ostream_t stream, const char *classname)
231 if (stream->last_class_stack_size > stream->curr_class_stack_size
232 && strcmp ((char *) gl_list_get_at (stream->class_stack,
233 stream->curr_class_stack_size),
235 emit_pending_spans (stream, true);
237 last_class_stack_size <= curr_class_stack_size
238 - in this case we have to append the given CLASSNAME -
240 last_class_stack_size > curr_class_stack_size
241 && class_stack[curr_class_stack_size] == CLASSNAME
242 - in this case we only need to increment curr_class_stack_size. */
243 if (stream->last_class_stack_size <= stream->curr_class_stack_size)
244 gl_list_add_at (stream->class_stack, stream->curr_class_stack_size,
245 xstrdup (classname));
246 stream->curr_class_stack_size++;
250 html_ostream::end_span (html_ostream_t stream, const char *classname)
252 if (!(stream->curr_class_stack_size > 0
253 && strcmp ((char *) gl_list_get_at (stream->class_stack,
254 stream->curr_class_stack_size - 1),
256 /* Improperly nested begin_span/end_span calls. */
258 stream->curr_class_stack_size--;
264 html_ostream_create (ostream_t destination)
266 html_ostream_t stream = XMALLOC (struct html_ostream_representation);
268 stream->base.vtable = &html_ostream_vtable;
269 stream->destination = destination;
270 stream->class_stack =
271 gl_list_create_empty (GL_ARRAY_LIST, NULL, NULL, NULL, true);
272 stream->curr_class_stack_size = 0;
273 stream->last_class_stack_size = 0;