(root)/
gettext-0.22.4/
libtextstyle/
lib/
html-ostream.c
       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-2020 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
      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
      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
     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
     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
     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 = (const 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
     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
     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
     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
     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 *
     366  html_ostream__get_hyperlink_ref (html_ostream_t stream)
     367  {
     368    return stream->hyperlink_ref;
     369  }
     370  
     371  static void
     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
     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
     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  /* Accessors.  */
     440  
     441  static ostream_t
     442  html_ostream__get_destination (html_ostream_t stream)
     443  {
     444    return stream->destination;
     445  }
     446  
     447  /* Instanceof test.  */
     448  
     449  bool
     450  is_instance_of_html_ostream (ostream_t stream)
     451  {
     452    return IS_INSTANCE (stream, ostream, html_ostream);
     453  }
     454  
     455  #line 456 "html-ostream.c"
     456  
     457  const struct html_ostream_implementation html_ostream_vtable =
     458  {
     459    html_ostream_superclasses,
     460    sizeof (html_ostream_superclasses) / sizeof (html_ostream_superclasses[0]),
     461    sizeof (struct html_ostream_representation),
     462    html_ostream__write_mem,
     463    html_ostream__flush,
     464    html_ostream__free,
     465    html_ostream__begin_span,
     466    html_ostream__end_span,
     467    html_ostream__get_hyperlink_ref,
     468    html_ostream__set_hyperlink_ref,
     469    html_ostream__flush_to_current_style,
     470    html_ostream__get_destination,
     471  };
     472  
     473  #if !HAVE_INLINE
     474  
     475  /* Define the functions that invoke the methods.  */
     476  
     477  void
     478  html_ostream_write_mem (html_ostream_t first_arg, const void *data, size_t len)
     479  {
     480    const struct html_ostream_implementation *vtable =
     481      ((struct html_ostream_representation_header *) (struct html_ostream_representation *) first_arg)->vtable;
     482    vtable->write_mem (first_arg,data,len);
     483  }
     484  
     485  void
     486  html_ostream_flush (html_ostream_t first_arg, ostream_flush_scope_t scope)
     487  {
     488    const struct html_ostream_implementation *vtable =
     489      ((struct html_ostream_representation_header *) (struct html_ostream_representation *) first_arg)->vtable;
     490    vtable->flush (first_arg,scope);
     491  }
     492  
     493  void
     494  html_ostream_free (html_ostream_t first_arg)
     495  {
     496    const struct html_ostream_implementation *vtable =
     497      ((struct html_ostream_representation_header *) (struct html_ostream_representation *) first_arg)->vtable;
     498    vtable->free (first_arg);
     499  }
     500  
     501  void
     502  html_ostream_begin_span (html_ostream_t first_arg, const char *classname)
     503  {
     504    const struct html_ostream_implementation *vtable =
     505      ((struct html_ostream_representation_header *) (struct html_ostream_representation *) first_arg)->vtable;
     506    vtable->begin_span (first_arg,classname);
     507  }
     508  
     509  void
     510  html_ostream_end_span (html_ostream_t first_arg, const char *classname)
     511  {
     512    const struct html_ostream_implementation *vtable =
     513      ((struct html_ostream_representation_header *) (struct html_ostream_representation *) first_arg)->vtable;
     514    vtable->end_span (first_arg,classname);
     515  }
     516  
     517  const char *
     518  html_ostream_get_hyperlink_ref (html_ostream_t first_arg)
     519  {
     520    const struct html_ostream_implementation *vtable =
     521      ((struct html_ostream_representation_header *) (struct html_ostream_representation *) first_arg)->vtable;
     522    return vtable->get_hyperlink_ref (first_arg);
     523  }
     524  
     525  void
     526  html_ostream_set_hyperlink_ref (html_ostream_t first_arg, const char *ref)
     527  {
     528    const struct html_ostream_implementation *vtable =
     529      ((struct html_ostream_representation_header *) (struct html_ostream_representation *) first_arg)->vtable;
     530    vtable->set_hyperlink_ref (first_arg,ref);
     531  }
     532  
     533  void
     534  html_ostream_flush_to_current_style (html_ostream_t first_arg)
     535  {
     536    const struct html_ostream_implementation *vtable =
     537      ((struct html_ostream_representation_header *) (struct html_ostream_representation *) first_arg)->vtable;
     538    vtable->flush_to_current_style (first_arg);
     539  }
     540  
     541  ostream_t
     542  html_ostream_get_destination (html_ostream_t first_arg)
     543  {
     544    const struct html_ostream_implementation *vtable =
     545      ((struct html_ostream_representation_header *) (struct html_ostream_representation *) first_arg)->vtable;
     546    return vtable->get_destination (first_arg);
     547  }
     548  
     549  #endif