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, "&lt;");
74           break;
75         case '>':
76           ostream_write_str (destination, "&gt;");
77           break;
78         case '&':
79           ostream_write_str (destination, "&amp;");
80           break;
81         case '"':
82           ostream_write_str (destination, "&quot;");
83           break;
84         case '\'':
85           ostream_write_str (destination, "&apos;");
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, "&quot;");
226                         break;
227                       case '&':
228                         ostream_write_str (stream->destination, "&amp;");
229                         break;
230                       case '<':
231                         ostream_write_str (stream->destination, "&lt;");
232                         break;
233                       case '>':
234                         /* Needed to avoid "]]>" in the output.  */
235                         ostream_write_str (stream->destination, "&gt;");
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, "&nbsp;");
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