1 /* DO NOT EDIT! GENERATED AUTOMATICALLY! */
2
3 #if !IS_CPLUSPLUS
4 #define html_ostream_representation any_ostream_representation
5 #endif
6 #line 1 "html-ostream.oo.c"
7 /* Output stream that produces HTML output.
8 Copyright (C) 2006-2009, 2019 Free Software Foundation, Inc.
9 Written by Bruno Haible <bruno@clisp.org>, 2006.
10
11 This program is free software: you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 3 of the License, or
14 (at your option) any later version.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with this program. If not, see <https://www.gnu.org/licenses/>. */
23
24 #include <config.h>
25
26 /* Specification. */
27 #include "html-ostream.h"
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32
33 #include "gl_xlist.h"
34 #include "gl_array_list.h"
35 #include "minmax.h"
36 #include "unistr.h"
37 #include "xalloc.h"
38
39 #line 40 "html-ostream.c"
40 #include "html_ostream.priv.h"
41
42 const typeinfo_t html_ostream_typeinfo = { "html_ostream" };
43
44 static const typeinfo_t * const html_ostream_superclasses[] =
45 { html_ostream_SUPERCLASSES };
46
47 #define super ostream_vtable
48
49 #line 51 "html-ostream.oo.c"
50
51 /* Emit an HTML attribute value.
52 quote is either '"' or '\''. */
53 static void
write_attribute_value(html_ostream_t stream,const char * value,char quote)54 write_attribute_value (html_ostream_t stream, const char *value, char quote)
55 {
56 /* Need to escape the '<', '>', '&', quote characters. */
57 ostream_t destination = stream->destination;
58 const char *p = value;
59
60 for (;;)
61 {
62 const char *q = p;
63
64 while (*q != '\0' && *q != '<' && *q != '>' && *q != '&' && *q != quote)
65 q++;
66 if (p < q)
67 ostream_write_mem (destination, p, q - p);
68 if (*q == '\0')
69 break;
70 switch (*q)
71 {
72 case '<':
73 ostream_write_str (destination, "<");
74 break;
75 case '>':
76 ostream_write_str (destination, ">");
77 break;
78 case '&':
79 ostream_write_str (destination, "&");
80 break;
81 case '"':
82 ostream_write_str (destination, """);
83 break;
84 case '\'':
85 ostream_write_str (destination, "'");
86 break;
87 default:
88 abort ();
89 }
90 p = q + 1;
91 }
92 }
93
94 /* Implementation of ostream_t methods. */
95
96 static void
verify_invariants(html_ostream_t stream)97 verify_invariants (html_ostream_t stream)
98 {
99 /* Verify the invariant regarding size(class_stack). */
100 if (gl_list_size (stream->class_stack)
101 != MAX (stream->curr_class_stack_size, stream->last_class_stack_size))
102 abort ();
103 }
104
105 /* Removes the excess elements of class_stack.
106 Needs to be called after max(curr_class_stack_size,last_class_stack_size)
107 may have been reduced. */
108 static void
shrink_class_stack(html_ostream_t stream)109 shrink_class_stack (html_ostream_t stream)
110 {
111 size_t keep =
112 MAX (stream->curr_class_stack_size, stream->last_class_stack_size);
113 size_t i = gl_list_size (stream->class_stack);
114 while (i > keep)
115 {
116 i--;
117 free ((char *) gl_list_get_at (stream->class_stack, i));
118 gl_list_remove_at (stream->class_stack, i);
119 }
120 }
121
122 /* Emits <span> or </span> tags, to follow the increase / decrease of the
123 class_stack from last_class_stack_size to curr_class_stack_size.
124 When done, sets last_class_stack_size to curr_class_stack_size. */
125 static void
emit_pending_spans(html_ostream_t stream,bool shrink_stack)126 emit_pending_spans (html_ostream_t stream, bool shrink_stack)
127 {
128 if (stream->curr_class_stack_size > stream->last_class_stack_size)
129 {
130 size_t i;
131
132 for (i = stream->last_class_stack_size; i < stream->curr_class_stack_size; i++)
133 {
134 char *classname = (char *) gl_list_get_at (stream->class_stack, i);
135
136 ostream_write_str (stream->destination, "<span class=\"");
137 ostream_write_str (stream->destination, classname);
138 ostream_write_str (stream->destination, "\">");
139 }
140 stream->last_class_stack_size = stream->curr_class_stack_size;
141 }
142 else if (stream->curr_class_stack_size < stream->last_class_stack_size)
143 {
144 size_t i;
145
146 for (i = stream->last_class_stack_size; i > stream->curr_class_stack_size; i--)
147 ostream_write_str (stream->destination, "</span>");
148 stream->last_class_stack_size = stream->curr_class_stack_size;
149 if (shrink_stack)
150 shrink_class_stack (stream);
151 }
152 /* Here last_class_stack_size == curr_class_stack_size. */
153 if (shrink_stack)
154 verify_invariants (stream);
155 }
156
157 static void
html_ostream__write_mem(html_ostream_t stream,const void * data,size_t len)158 html_ostream__write_mem (html_ostream_t stream, const void *data, size_t len)
159 {
160 if (len > 0)
161 {
162 #define BUFFERSIZE 2048
163 char inbuffer[BUFFERSIZE];
164 size_t inbufcount;
165
166 inbufcount = stream->buflen;
167 if (inbufcount > 0)
168 memcpy (inbuffer, stream->buf, inbufcount);
169 for (;;)
170 {
171 /* At this point, inbuffer[0..inbufcount-1] is filled. */
172 {
173 /* Combine the previous rest with a chunk of new input. */
174 size_t n =
175 (len <= BUFFERSIZE - inbufcount ? len : BUFFERSIZE - inbufcount);
176
177 if (n > 0)
178 {
179 memcpy (inbuffer + inbufcount, data, n);
180 data = (char *) data + n;
181 inbufcount += n;
182 len -= n;
183 }
184 }
185 {
186 /* Handle complete UTF-8 characters. */
187 const char *inptr = inbuffer;
188 size_t insize = inbufcount;
189
190 while (insize > 0)
191 {
192 unsigned char c0;
193 ucs4_t uc;
194 int nbytes;
195
196 c0 = ((const unsigned char *) inptr)[0];
197 if (insize < (c0 < 0xc0 ? 1 : c0 < 0xe0 ? 2 : c0 < 0xf0 ? 3 :
198 c0 < 0xf8 ? 4 : c0 < 0xfc ? 5 : 6))
199 break;
200
201 nbytes = u8_mbtouc (&uc, (const unsigned char *) inptr, insize);
202
203 if (uc == '\n')
204 {
205 verify_invariants (stream);
206 /* Emit </span> tags to follow the decrease of the class stack
207 from last_class_stack_size to 0. Then emit the newline.
208 Then prepare for emitting <span> tags to go back from 0
209 to curr_class_stack_size. */
210 size_t prev_class_stack_size = stream->curr_class_stack_size;
211 stream->curr_class_stack_size = 0;
212 emit_pending_spans (stream, false);
213 stream->curr_class_stack_size = prev_class_stack_size;
214 ostream_write_str (stream->destination, "<br/>");
215 shrink_class_stack (stream);
216 verify_invariants (stream);
217 }
218 else
219 {
220 emit_pending_spans (stream, true);
221
222 switch (uc)
223 {
224 case '"':
225 ostream_write_str (stream->destination, """);
226 break;
227 case '&':
228 ostream_write_str (stream->destination, "&");
229 break;
230 case '<':
231 ostream_write_str (stream->destination, "<");
232 break;
233 case '>':
234 /* Needed to avoid "]]>" in the output. */
235 ostream_write_str (stream->destination, ">");
236 break;
237 case ' ':
238 /* Needed because HTML viewers merge adjacent spaces
239 and drop spaces adjacent to <br> and similar. */
240 ostream_write_str (stream->destination, " ");
241 break;
242 default:
243 if (uc >= 0x20 && uc < 0x7F)
244 {
245 /* Output ASCII characters as such. */
246 char bytes[1];
247 bytes[0] = uc;
248 ostream_write_mem (stream->destination, bytes, 1);
249 }
250 else
251 {
252 /* Output non-ASCII characters in #&nnn;
253 notation. */
254 char bytes[32];
255 sprintf (bytes, "&#%d;", (int) uc);
256 ostream_write_str (stream->destination, bytes);
257 }
258 break;
259 }
260 }
261
262 inptr += nbytes;
263 insize -= nbytes;
264 }
265 /* Put back the unconverted part. */
266 if (insize > BUFSIZE)
267 abort ();
268 if (len == 0)
269 {
270 if (insize > 0)
271 memcpy (stream->buf, inptr, insize);
272 stream->buflen = insize;
273 break;
274 }
275 if (insize > 0)
276 memmove (inbuffer, inptr, insize);
277 inbufcount = insize;
278 }
279 }
280 #undef BUFFERSIZE
281 }
282 }
283
284 static void
html_ostream__flush(html_ostream_t stream,ostream_flush_scope_t scope)285 html_ostream__flush (html_ostream_t stream, ostream_flush_scope_t scope)
286 {
287 verify_invariants (stream);
288 /* stream->buf[] contains only a few bytes that don't correspond to a
289 character. Can't flush it. */
290 /* Close the open <span> tags, and prepare for reopening the same <span>
291 tags. */
292 size_t prev_class_stack_size = stream->curr_class_stack_size;
293 stream->curr_class_stack_size = 0;
294 emit_pending_spans (stream, false);
295 stream->curr_class_stack_size = prev_class_stack_size;
296 shrink_class_stack (stream);
297 verify_invariants (stream);
298
299 if (scope != FLUSH_THIS_STREAM)
300 ostream_flush (stream->destination, scope);
301 }
302
303 static void
html_ostream__free(html_ostream_t stream)304 html_ostream__free (html_ostream_t stream)
305 {
306 stream->curr_class_stack_size = 0;
307 emit_pending_spans (stream, true);
308 if (stream->hyperlink_ref != NULL)
309 {
310 /* Close the current <a> element. */
311 ostream_write_str (stream->destination, "</a>");
312 free (stream->hyperlink_ref);
313 }
314 verify_invariants (stream);
315 gl_list_free (stream->class_stack);
316 free (stream);
317 }
318
319 /* Implementation of html_ostream_t methods. */
320
321 static void
html_ostream__begin_span(html_ostream_t stream,const char * classname)322 html_ostream__begin_span (html_ostream_t stream, const char *classname)
323 {
324 verify_invariants (stream);
325 if (stream->last_class_stack_size > stream->curr_class_stack_size
326 && strcmp ((char *) gl_list_get_at (stream->class_stack,
327 stream->curr_class_stack_size),
328 classname) != 0)
329 emit_pending_spans (stream, true);
330 /* Now either
331 last_class_stack_size <= curr_class_stack_size
332 - in this case we have to append the given CLASSNAME -
333 or
334 last_class_stack_size > curr_class_stack_size
335 && class_stack[curr_class_stack_size] == CLASSNAME
336 - in this case we only need to increment curr_class_stack_size. */
337 if (stream->last_class_stack_size <= stream->curr_class_stack_size)
338 gl_list_add_at (stream->class_stack, stream->curr_class_stack_size,
339 xstrdup (classname));
340 stream->curr_class_stack_size++;
341 verify_invariants (stream);
342 }
343
344 static void
html_ostream__end_span(html_ostream_t stream,const char * classname)345 html_ostream__end_span (html_ostream_t stream, const char *classname)
346 {
347 verify_invariants (stream);
348 if (stream->curr_class_stack_size > 0)
349 {
350 char *innermost_active_span =
351 (char *) gl_list_get_at (stream->class_stack,
352 stream->curr_class_stack_size - 1);
353 if (strcmp (innermost_active_span, classname) == 0)
354 {
355 stream->curr_class_stack_size--;
356 shrink_class_stack (stream);
357 verify_invariants (stream);
358 return;
359 }
360 }
361 /* Improperly nested begin_span/end_span calls. */
362 abort ();
363 }
364
365 static const char *
html_ostream__get_hyperlink_ref(html_ostream_t stream)366 html_ostream__get_hyperlink_ref (html_ostream_t stream)
367 {
368 return stream->hyperlink_ref;
369 }
370
371 static void
html_ostream__set_hyperlink_ref(html_ostream_t stream,const char * ref)372 html_ostream__set_hyperlink_ref (html_ostream_t stream, const char *ref)
373 {
374 char *ref_copy = (ref != NULL ? xstrdup (ref) : NULL);
375
376 verify_invariants (stream);
377 if (stream->hyperlink_ref != NULL)
378 {
379 /* Close the open <span> tags, and prepare for reopening the same <span>
380 tags. */
381 size_t prev_class_stack_size = stream->curr_class_stack_size;
382 stream->curr_class_stack_size = 0;
383 emit_pending_spans (stream, false);
384 stream->curr_class_stack_size = prev_class_stack_size;
385 /* Close the current <a> element. */
386 ostream_write_str (stream->destination, "</a>");
387 shrink_class_stack (stream);
388
389 free (stream->hyperlink_ref);
390 }
391 stream->hyperlink_ref = ref_copy;
392 if (stream->hyperlink_ref != NULL)
393 {
394 /* Close the open <span> tags, and prepare for reopening the same <span>
395 tags. */
396 size_t prev_class_stack_size = stream->curr_class_stack_size;
397 stream->curr_class_stack_size = 0;
398 emit_pending_spans (stream, false);
399 stream->curr_class_stack_size = prev_class_stack_size;
400 /* Open an <a> element. */
401 ostream_write_str (stream->destination, "<a href=\"");
402 write_attribute_value (stream, stream->hyperlink_ref, '"');
403 ostream_write_str (stream->destination, "\">");
404 shrink_class_stack (stream);
405 }
406 verify_invariants (stream);
407 }
408
409 static void
html_ostream__flush_to_current_style(html_ostream_t stream)410 html_ostream__flush_to_current_style (html_ostream_t stream)
411 {
412 verify_invariants (stream);
413 /* stream->buf[] contains only a few bytes that don't correspond to a
414 character. Can't flush it. */
415 /* Open all requested <span> tags. */
416 emit_pending_spans (stream, true);
417 verify_invariants (stream);
418 }
419
420 /* Constructor. */
421
422 html_ostream_t
html_ostream_create(ostream_t destination)423 html_ostream_create (ostream_t destination)
424 {
425 html_ostream_t stream = XMALLOC (struct html_ostream_representation);
426
427 stream->base.vtable = &html_ostream_vtable;
428 stream->destination = destination;
429 stream->hyperlink_ref = NULL;
430 stream->class_stack =
431 gl_list_create_empty (GL_ARRAY_LIST, NULL, NULL, NULL, true);
432 stream->curr_class_stack_size = 0;
433 stream->last_class_stack_size = 0;
434 stream->buflen = 0;
435
436 return stream;
437 }
438
439 #line 440 "html-ostream.c"
440
441 const struct html_ostream_implementation html_ostream_vtable =
442 {
443 html_ostream_superclasses,
444 sizeof (html_ostream_superclasses) / sizeof (html_ostream_superclasses[0]),
445 sizeof (struct html_ostream_representation),
446 html_ostream__write_mem,
447 html_ostream__flush,
448 html_ostream__free,
449 html_ostream__begin_span,
450 html_ostream__end_span,
451 html_ostream__get_hyperlink_ref,
452 html_ostream__set_hyperlink_ref,
453 html_ostream__flush_to_current_style,
454 };
455
456 #if !HAVE_INLINE
457
458 /* Define the functions that invoke the methods. */
459
460 void
html_ostream_write_mem(html_ostream_t first_arg,const void * data,size_t len)461 html_ostream_write_mem (html_ostream_t first_arg, const void *data, size_t len)
462 {
463 const struct html_ostream_implementation *vtable =
464 ((struct html_ostream_representation_header *) (struct html_ostream_representation *) first_arg)->vtable;
465 vtable->write_mem (first_arg,data,len);
466 }
467
468 void
html_ostream_flush(html_ostream_t first_arg,ostream_flush_scope_t scope)469 html_ostream_flush (html_ostream_t first_arg, ostream_flush_scope_t scope)
470 {
471 const struct html_ostream_implementation *vtable =
472 ((struct html_ostream_representation_header *) (struct html_ostream_representation *) first_arg)->vtable;
473 vtable->flush (first_arg,scope);
474 }
475
476 void
html_ostream_free(html_ostream_t first_arg)477 html_ostream_free (html_ostream_t first_arg)
478 {
479 const struct html_ostream_implementation *vtable =
480 ((struct html_ostream_representation_header *) (struct html_ostream_representation *) first_arg)->vtable;
481 vtable->free (first_arg);
482 }
483
484 void
html_ostream_begin_span(html_ostream_t first_arg,const char * classname)485 html_ostream_begin_span (html_ostream_t first_arg, const char *classname)
486 {
487 const struct html_ostream_implementation *vtable =
488 ((struct html_ostream_representation_header *) (struct html_ostream_representation *) first_arg)->vtable;
489 vtable->begin_span (first_arg,classname);
490 }
491
492 void
html_ostream_end_span(html_ostream_t first_arg,const char * classname)493 html_ostream_end_span (html_ostream_t first_arg, const char *classname)
494 {
495 const struct html_ostream_implementation *vtable =
496 ((struct html_ostream_representation_header *) (struct html_ostream_representation *) first_arg)->vtable;
497 vtable->end_span (first_arg,classname);
498 }
499
500 const char *
html_ostream_get_hyperlink_ref(html_ostream_t first_arg)501 html_ostream_get_hyperlink_ref (html_ostream_t first_arg)
502 {
503 const struct html_ostream_implementation *vtable =
504 ((struct html_ostream_representation_header *) (struct html_ostream_representation *) first_arg)->vtable;
505 return vtable->get_hyperlink_ref (first_arg);
506 }
507
508 void
html_ostream_set_hyperlink_ref(html_ostream_t first_arg,const char * ref)509 html_ostream_set_hyperlink_ref (html_ostream_t first_arg, const char *ref)
510 {
511 const struct html_ostream_implementation *vtable =
512 ((struct html_ostream_representation_header *) (struct html_ostream_representation *) first_arg)->vtable;
513 vtable->set_hyperlink_ref (first_arg,ref);
514 }
515
516 void
html_ostream_flush_to_current_style(html_ostream_t first_arg)517 html_ostream_flush_to_current_style (html_ostream_t first_arg)
518 {
519 const struct html_ostream_implementation *vtable =
520 ((struct html_ostream_representation_header *) (struct html_ostream_representation *) first_arg)->vtable;
521 vtable->flush_to_current_style (first_arg);
522 }
523
524 #endif
525