• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500-V1.0.1.40_1.0.68/ap/gpl/timemachine/gettext-0.17/gettext-tools/gnulib-lib/
1/* Output stream that produces HTML output.
2   Copyright (C) 2006-2007 Free Software Foundation, Inc.
3   Written by Bruno Haible <bruno@clisp.org>, 2006.
4
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.
9
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.
14
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/>.  */
17
18#include <config.h>
19
20/* Specification.  */
21#include "html-ostream.h"
22
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26
27#include "gl_list.h"
28#include "gl_array_list.h"
29#include "unistr.h"
30#include "xalloc.h"
31
32struct html_ostream : struct ostream
33{
34fields:
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.  */
44  #define BUFSIZE 6
45  char buf[BUFSIZE];
46  size_t buflen;
47};
48
49/* Implementation of ostream_t methods.  */
50
51static void
52emit_pending_spans (html_ostream_t stream, bool shrink_stack)
53{
54  if (stream->curr_class_stack_size > stream->last_class_stack_size)
55    {
56      size_t i;
57
58      for (i = stream->last_class_stack_size; i < stream->curr_class_stack_size; i++)
59	{
60	  char *classname = (char *) gl_list_get_at (stream->class_stack, i);
61
62	  ostream_write_str (stream->destination, "<span class=\"");
63	  ostream_write_str (stream->destination, classname);
64	  ostream_write_str (stream->destination, "\">");
65	}
66      stream->last_class_stack_size = stream->curr_class_stack_size;
67    }
68  else if (stream->curr_class_stack_size < stream->last_class_stack_size)
69    {
70      size_t i = stream->last_class_stack_size;
71
72      while (i > stream->curr_class_stack_size)
73	{
74	  char *classname;
75
76	  --i;
77	  classname = (char *) gl_list_get_at (stream->class_stack, i);
78	  ostream_write_str (stream->destination, "</span>");
79	  if (shrink_stack)
80	    {
81	      gl_list_remove_at (stream->class_stack, i);
82	      free (classname);
83	    }
84	}
85      stream->last_class_stack_size = stream->curr_class_stack_size;
86    }
87}
88
89static void
90html_ostream::write_mem (html_ostream_t stream, const void *data, size_t len)
91{
92  if (len > 0)
93    {
94      #define BUFFERSIZE 2048
95      char inbuffer[BUFFERSIZE];
96      size_t inbufcount;
97
98      inbufcount = stream->buflen;
99      if (inbufcount > 0)
100	memcpy (inbuffer, stream->buf, inbufcount);
101      for (;;)
102	{
103	  /* At this point, inbuffer[0..inbufcount-1] is filled.  */
104	  {
105	    /* Combine the previous rest with a chunk of new input.  */
106	    size_t n =
107	      (len <= BUFFERSIZE - inbufcount ? len : BUFFERSIZE - inbufcount);
108
109	    if (n > 0)
110	      {
111		memcpy (inbuffer + inbufcount, data, n);
112		data = (char *) data + n;
113		inbufcount += n;
114		len -= n;
115	      }
116	  }
117	  {
118	    /* Handle complete UTF-8 characters.  */
119	    const char *inptr = inbuffer;
120	    size_t insize = inbufcount;
121
122	    while (insize > 0)
123	      {
124		unsigned char c0;
125		unsigned int uc;
126		int nbytes;
127
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))
131		  break;
132
133		nbytes = u8_mbtouc (&uc, (const unsigned char *) inptr, insize);
134
135		if (uc == '\n')
136		  {
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;
142		  }
143		else
144		  {
145		    emit_pending_spans (stream, true);
146
147		    switch (uc)
148		      {
149		      case '"':
150			ostream_write_str (stream->destination, "&quot;");
151			break;
152		      case '&':
153			ostream_write_str (stream->destination, "&amp;");
154			break;
155		      case '<':
156			ostream_write_str (stream->destination, "&lt;");
157			break;
158		      case '>':
159			/* Needed to avoid "]]>" in the output.  */
160			ostream_write_str (stream->destination, "&gt;");
161			break;
162		      case ' ':
163			/* Needed because HTML viewers merge adjacent spaces
164			   and drop spaces adjacent to <br> and similar.  */
165			ostream_write_str (stream->destination, "&nbsp;");
166			break;
167		      default:
168			if (uc >= 0x20 && uc < 0x7F)
169			  {
170			    /* Output ASCII characters as such.  */
171			    char bytes[1];
172			    bytes[0] = uc;
173			    ostream_write_mem (stream->destination, bytes, 1);
174			  }
175			else
176			  {
177			    /* Output non-ASCII characters in #&nnn;
178			       notation.  */
179			    char bytes[32];
180			    sprintf (bytes, "&#%d;", uc);
181			    ostream_write_str (stream->destination, bytes);
182			  }
183			break;
184		      }
185		  }
186
187		inptr += nbytes;
188		insize -= nbytes;
189	      }
190	    /* Put back the unconverted part.  */
191	    if (insize > BUFSIZE)
192	      abort ();
193	    if (len == 0)
194	      {
195		if (insize > 0)
196		  memcpy (stream->buf, inptr, insize);
197		stream->buflen = insize;
198		break;
199	      }
200	    if (insize > 0)
201	      memmove (inbuffer, inptr, insize);
202	    inbufcount = insize;
203	  }
204	}
205      #undef BUFFERSIZE
206    }
207}
208
209static void
210html_ostream::flush (html_ostream_t stream)
211{
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
214     the open spans.  */
215}
216
217static void
218html_ostream::free (html_ostream_t stream)
219{
220  stream->curr_class_stack_size = 0;
221  emit_pending_spans (stream, true);
222  gl_list_free (stream->class_stack);
223  free (stream);
224}
225
226/* Implementation of html_ostream_t methods.  */
227
228void
229html_ostream::begin_span (html_ostream_t stream, const char *classname)
230{
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),
234		 classname) != 0)
235    emit_pending_spans (stream, true);
236  /* Now either
237       last_class_stack_size <= curr_class_stack_size
238       - in this case we have to append the given CLASSNAME -
239     or
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++;
247}
248
249void
250html_ostream::end_span (html_ostream_t stream, const char *classname)
251{
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),
255		   classname) == 0))
256    /* Improperly nested begin_span/end_span calls.  */
257    abort ();
258  stream->curr_class_stack_size--;
259}
260
261/* Constructor.  */
262
263html_ostream_t
264html_ostream_create (ostream_t destination)
265{
266  html_ostream_t stream = XMALLOC (struct html_ostream_representation);
267
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;
274  stream->buflen = 0;
275
276  return stream;
277}
278