Imported Upstream version 0.19.7
[platform/upstream/gettext.git] / gettext-tools / gnulib-lib / term-styled-ostream.c
1 /* DO NOT EDIT! GENERATED AUTOMATICALLY! */
2
3 #line 1 "term-styled-ostream.oo.c"
4 /* Output stream for CSS styled text, producing ANSI escape sequences.
5    Copyright (C) 2006-2007, 2015 Free Software Foundation, Inc.
6    Written by Bruno Haible <bruno@clisp.org>, 2006.
7
8    This program is free software: you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
20
21 #include <config.h>
22
23 /* Specification.  */
24 #include "term-styled-ostream.h"
25
26 #include <stdlib.h>
27
28 #include <cr-om-parser.h>
29 #include <cr-sel-eng.h>
30 #include <cr-style.h>
31 #include <cr-rgb.h>
32 /* <cr-fonts.h> has a broken double-inclusion guard in libcroco-0.6.1.  */
33 #ifndef __CR_FONTS_H__
34 # include <cr-fonts.h>
35 #endif
36 #include <cr-string.h>
37
38 #include "term-ostream.h"
39 #include "hash.h"
40 #include "xalloc.h"
41
42
43 /* CSS matching works as follows:
44    Suppose we have an element inside class "header" inside class "table".
45    We pretend to have an XML tree that looks like this:
46
47      (root)
48        +----table
49               +----header
50
51    For each of these XML nodes, the CSS matching engine can report the
52    matching CSS declarations.  We extract the CSS property values that
53    matter for terminal styling and cache them.  */
54
55 /* Attributes that can be set on a character.  */
56 typedef struct
57 {
58   term_color_t     color;
59   term_color_t     bgcolor;
60   term_weight_t    weight;
61   term_posture_t   posture;
62   term_underline_t underline;
63 } attributes_t;
64
65 #line 66 "term-styled-ostream.c"
66 #if !IS_CPLUSPLUS
67 #define term_styled_ostream_representation any_ostream_representation
68 #endif
69 #include "term_styled_ostream.priv.h"
70
71 const typeinfo_t term_styled_ostream_typeinfo = { "term_styled_ostream" };
72
73 static const typeinfo_t * const term_styled_ostream_superclasses[] =
74   { term_styled_ostream_SUPERCLASSES };
75
76 #define super styled_ostream_vtable
77
78 #line 82 "term-styled-ostream.oo.c"
79
80 /* Implementation of ostream_t methods.  */
81
82 static void
83 term_styled_ostream__write_mem (term_styled_ostream_t stream,
84                                 const void *data, size_t len)
85 {
86   term_ostream_set_color (stream->destination, stream->curr_attr->color);
87   term_ostream_set_bgcolor (stream->destination, stream->curr_attr->bgcolor);
88   term_ostream_set_weight (stream->destination, stream->curr_attr->weight);
89   term_ostream_set_posture (stream->destination, stream->curr_attr->posture);
90   term_ostream_set_underline (stream->destination, stream->curr_attr->underline);
91
92   term_ostream_write_mem (stream->destination, data, len);
93 }
94
95 static void
96 term_styled_ostream__flush (term_styled_ostream_t stream)
97 {
98   term_ostream_flush (stream->destination);
99 }
100
101 static void
102 term_styled_ostream__free (term_styled_ostream_t stream)
103 {
104   term_ostream_free (stream->destination);
105   cr_cascade_destroy (stream->css_document);
106   cr_sel_eng_destroy (stream->css_engine);
107   free (stream->curr_classes);
108   {
109     void *ptr = NULL;
110     const void *key;
111     size_t keylen;
112     void *data;
113
114     while (hash_iterate (&stream->cache, &ptr, &key, &keylen, &data) == 0)
115       {
116         free (data);
117       }
118   }
119   hash_destroy (&stream->cache);
120   free (stream);
121 }
122
123 /* Implementation of styled_ostream_t methods.  */
124
125 /* CRStyle doesn't contain a value for the 'text-decoration' property.
126    So we have to extend it.  */
127
128 enum CRXTextDecorationType
129 {
130   TEXT_DECORATION_NONE,
131   TEXT_DECORATION_UNDERLINE,
132   TEXT_DECORATION_OVERLINE,
133   TEXT_DECORATION_LINE_THROUGH,
134   TEXT_DECORATION_BLINK,
135   TEXT_DECORATION_INHERIT
136 };
137
138 typedef struct _CRXStyle
139 {
140   struct _CRXStyle *parent_style;
141   CRStyle *base;
142   enum CRXTextDecorationType text_decoration;
143 } CRXStyle;
144
145 /* An extended version of cr_style_new.  */
146 static CRXStyle *
147 crx_style_new (gboolean a_set_props_to_initial_values)
148 {
149   CRStyle *base;
150   CRXStyle *result;
151
152   base = cr_style_new (a_set_props_to_initial_values);
153   if (base == NULL)
154     return NULL;
155
156   result = XMALLOC (CRXStyle);
157   result->base = base;
158   if (a_set_props_to_initial_values)
159     result->text_decoration = TEXT_DECORATION_NONE;
160   else
161     result->text_decoration = TEXT_DECORATION_INHERIT;
162
163   return result;
164 }
165
166 /* An extended version of cr_style_destroy.  */
167 static void
168 crx_style_destroy (CRXStyle *a_style)
169 {
170   cr_style_destroy (a_style->base);
171   free (a_style);
172 }
173
174 /* An extended version of cr_sel_eng_get_matched_style.  */
175 static enum CRStatus
176 crx_sel_eng_get_matched_style (CRSelEng * a_this, CRCascade * a_cascade,
177                                xmlNode * a_node,
178                                CRXStyle * a_parent_style, CRXStyle ** a_style,
179                                gboolean a_set_props_to_initial_values)
180 {
181   enum CRStatus status;
182   CRPropList *props = NULL;
183
184   if (!(a_this && a_cascade && a_node && a_style))
185     return CR_BAD_PARAM_ERROR;
186
187   status = cr_sel_eng_get_matched_properties_from_cascade (a_this, a_cascade,
188                                                            a_node, &props);
189   if (!(status == CR_OK))
190     return status;
191
192   if (props)
193     {
194       CRXStyle *style;
195
196       if (!*a_style)
197         {
198           *a_style = crx_style_new (a_set_props_to_initial_values);
199           if (!*a_style)
200             return CR_ERROR;
201         }
202       else
203         {
204           if (a_set_props_to_initial_values)
205             {
206               cr_style_set_props_to_initial_values ((*a_style)->base);
207               (*a_style)->text_decoration = TEXT_DECORATION_NONE;
208             }
209           else
210             {
211               cr_style_set_props_to_default_values ((*a_style)->base);
212               (*a_style)->text_decoration = TEXT_DECORATION_INHERIT;
213             }
214         }
215       style = *a_style;
216       style->parent_style = a_parent_style;
217       style->base->parent_style =
218         (a_parent_style != NULL ? a_parent_style->base : NULL);
219
220       {
221         CRPropList *cur;
222
223         for (cur = props; cur != NULL; cur = cr_prop_list_get_next (cur))
224           {
225             CRDeclaration *decl = NULL;
226
227             cr_prop_list_get_decl (cur, &decl);
228             cr_style_set_style_from_decl (style->base, decl);
229             if (decl != NULL
230                 && decl->property != NULL
231                 && decl->property->stryng != NULL
232                 && decl->property->stryng->str != NULL)
233               {
234                 if (strcmp (decl->property->stryng->str, "text-decoration") == 0
235                     && decl->value != NULL
236                     && decl->value->type == TERM_IDENT
237                     && decl->value->content.str != NULL)
238                   {
239                     const char *value =
240                       cr_string_peek_raw_str (decl->value->content.str);
241
242                     if (value != NULL)
243                       {
244                         if (strcmp (value, "none") == 0)
245                           style->text_decoration = TEXT_DECORATION_NONE;
246                         else if (strcmp (value, "underline") == 0)
247                           style->text_decoration = TEXT_DECORATION_UNDERLINE;
248                         else if (strcmp (value, "overline") == 0)
249                           style->text_decoration = TEXT_DECORATION_OVERLINE;
250                         else if (strcmp (value, "line-through") == 0)
251                           style->text_decoration = TEXT_DECORATION_LINE_THROUGH;
252                         else if (strcmp (value, "blink") == 0)
253                           style->text_decoration = TEXT_DECORATION_BLINK;
254                         else if (strcmp (value, "inherit") == 0)
255                           style->text_decoration = TEXT_DECORATION_INHERIT;
256                       }
257                   }
258               }
259           }
260       }
261
262       cr_prop_list_destroy (props);
263     }
264
265   return CR_OK;
266 }
267
268 /* According to the CSS2 spec, sections 6.1 and 6.2, we need to do a
269    propagation: specified values -> computed values -> actual values.
270    The computed values are necessary.  libcroco does not compute them for us.
271    The function cr_style_resolve_inherited_properties is also not sufficient:
272    it handles only the case of inheritance, not the case of non-inheritance.
273    So we write style accessors that fetch the computed value, doing the
274    inheritance on the fly.
275    We then compute the actual values from the computed values; for colors,
276    this is done through the rgb_to_color method.  */
277
278 static term_color_t
279 style_compute_color_value (CRStyle *style, enum CRRgbProp which,
280                            term_ostream_t stream)
281 {
282   for (;;)
283     {
284       if (style == NULL)
285         return COLOR_DEFAULT;
286       if (cr_rgb_is_set_to_inherit (&style->rgb_props[which].sv))
287         style = style->parent_style;
288       else if (cr_rgb_is_set_to_transparent (&style->rgb_props[which].sv))
289         /* A transparent color occurs as default background color, set by
290            cr_style_set_props_to_default_values.  */
291         return COLOR_DEFAULT;
292       else
293         {
294           CRRgb rgb;
295           int r;
296           int g;
297           int b;
298
299           cr_rgb_copy (&rgb, &style->rgb_props[which].sv);
300           if (cr_rgb_compute_from_percentage (&rgb) != CR_OK)
301             abort ();
302           r = rgb.red & 0xff;
303           g = rgb.green & 0xff;
304           b = rgb.blue & 0xff;
305           return term_ostream_rgb_to_color (stream, r, g, b);
306         }
307     }
308 }
309
310 static term_weight_t
311 style_compute_font_weight_value (const CRStyle *style)
312 {
313   int value = 0;
314   for (;;)
315     {
316       if (style == NULL)
317         value += 4;
318       else
319         switch (style->font_weight)
320           {
321           case FONT_WEIGHT_INHERIT:
322             style = style->parent_style;
323             continue;
324           case FONT_WEIGHT_BOLDER:
325             value += 1;
326             style = style->parent_style;
327             continue;
328           case FONT_WEIGHT_LIGHTER:
329             value -= 1;
330             style = style->parent_style;
331             continue;
332           case FONT_WEIGHT_100:
333             value += 1;
334             break;
335           case FONT_WEIGHT_200:
336             value += 2;
337             break;
338           case FONT_WEIGHT_300:
339             value += 3;
340             break;
341           case FONT_WEIGHT_400: case FONT_WEIGHT_NORMAL:
342             value += 4;
343             break;
344           case FONT_WEIGHT_500:
345             value += 5;
346             break;
347           case FONT_WEIGHT_600:
348             value += 6;
349             break;
350           case FONT_WEIGHT_700: case FONT_WEIGHT_BOLD:
351             value += 7;
352             break;
353           case FONT_WEIGHT_800:
354             value += 8;
355             break;
356           case FONT_WEIGHT_900:
357             value += 9;
358             break;
359           default:
360             abort ();
361           }
362       /* Value >= 600 -> WEIGHT_BOLD.  Value <= 500 -> WEIGHT_NORMAL.  */
363       return (value >= 6 ? WEIGHT_BOLD : WEIGHT_NORMAL);
364     }
365 }
366
367 static term_posture_t
368 style_compute_font_posture_value (const CRStyle *style)
369 {
370   for (;;)
371     {
372       if (style == NULL)
373         return POSTURE_DEFAULT;
374       switch (style->font_style)
375         {
376         case FONT_STYLE_INHERIT:
377           style = style->parent_style;
378           break;
379         case FONT_STYLE_NORMAL:
380           return POSTURE_NORMAL;
381         case FONT_STYLE_ITALIC:
382         case FONT_STYLE_OBLIQUE:
383           return POSTURE_ITALIC;
384         default:
385           abort ();
386         }
387     }
388 }
389
390 static term_underline_t
391 style_compute_text_underline_value (const CRXStyle *style)
392 {
393   for (;;)
394     {
395       if (style == NULL)
396         return UNDERLINE_DEFAULT;
397       switch (style->text_decoration)
398         {
399         case TEXT_DECORATION_INHERIT:
400           style = style->parent_style;
401           break;
402         case TEXT_DECORATION_NONE:
403         case TEXT_DECORATION_OVERLINE:
404         case TEXT_DECORATION_LINE_THROUGH:
405         case TEXT_DECORATION_BLINK:
406           return UNDERLINE_OFF;
407         case TEXT_DECORATION_UNDERLINE:
408           return UNDERLINE_ON;
409         default:
410           abort ();
411         }
412     }
413 }
414
415 /* Match the current list of CSS classes to the CSS and return the result.  */
416 static attributes_t *
417 match (term_styled_ostream_t stream)
418 {
419   xmlNodePtr root;
420   xmlNodePtr curr;
421   char *p_end;
422   char *p_start;
423   CRXStyle *curr_style;
424   CRStyle *curr_style_base;
425   attributes_t *attr;
426
427   /* Create a hierarchy of XML nodes.  */
428   root = xmlNewNode (NULL, (const xmlChar *) "__root__");
429   root->type = XML_ELEMENT_NODE;
430   curr = root;
431   p_end = &stream->curr_classes[stream->curr_classes_length];
432   p_start = stream->curr_classes;
433   while (p_start < p_end)
434     {
435       char *p;
436       xmlNodePtr child;
437
438       if (!(*p_start == ' '))
439         abort ();
440       p_start++;
441       for (p = p_start; p < p_end && *p != ' '; p++)
442         ;
443
444       /* Temporarily replace the ' ' by '\0'.  */
445       *p = '\0';
446       child = xmlNewNode (NULL, (const xmlChar *) p_start);
447       child->type = XML_ELEMENT_NODE;
448       xmlSetProp (child, (const xmlChar *) "class", (const xmlChar *) p_start);
449       *p = ' ';
450
451       if (xmlAddChild (curr, child) == NULL)
452         /* Error! Shouldn't happen.  */
453         abort ();
454
455       curr = child;
456       p_start = p;
457     }
458
459   /* Retrieve the matching CSS declarations.  */
460   /* Not curr_style = crx_style_new (TRUE); because that assumes that the
461      default foreground color is black and that the default background color
462      is white, which is not necessarily true in a terminal context.  */
463   curr_style = NULL;
464   for (curr = root; curr != NULL; curr = curr->children)
465     {
466       CRXStyle *parent_style = curr_style;
467       curr_style = NULL;
468
469       if (crx_sel_eng_get_matched_style (stream->css_engine,
470                                          stream->css_document,
471                                          curr,
472                                          parent_style, &curr_style,
473                                          FALSE) != CR_OK)
474         abort ();
475       if (curr_style == NULL)
476         /* No declarations matched this node.  Inherit all values.  */
477         curr_style = parent_style;
478       else
479         /* curr_style is a new style, inheriting from parent_style.  */
480         ;
481     }
482   curr_style_base = (curr_style != NULL ? curr_style->base : NULL);
483
484   /* Extract the CSS declarations that we can use.  */
485   attr = XMALLOC (attributes_t);
486   attr->color =
487     style_compute_color_value (curr_style_base, RGB_PROP_COLOR,
488                                stream->destination);
489   attr->bgcolor =
490     style_compute_color_value (curr_style_base, RGB_PROP_BACKGROUND_COLOR,
491                                stream->destination);
492   attr->weight = style_compute_font_weight_value (curr_style_base);
493   attr->posture = style_compute_font_posture_value (curr_style_base);
494   attr->underline = style_compute_text_underline_value (curr_style);
495
496   /* Free the style chain.  */
497   while (curr_style != NULL)
498     {
499       CRXStyle *parent_style = curr_style->parent_style;
500
501       crx_style_destroy (curr_style);
502       curr_style = parent_style;
503     }
504
505   /* Free the XML nodes.  */
506   xmlFreeNodeList (root);
507
508   return attr;
509 }
510
511 /* Match the current list of CSS classes to the CSS and store the result in
512    stream->curr_attr and in the cache.  */
513 static void
514 match_and_cache (term_styled_ostream_t stream)
515 {
516   attributes_t *attr = match (stream);
517   if (hash_insert_entry (&stream->cache,
518                          stream->curr_classes, stream->curr_classes_length,
519                          attr) == NULL)
520     abort ();
521   stream->curr_attr = attr;
522 }
523
524 static void
525 term_styled_ostream__begin_use_class (term_styled_ostream_t stream,
526                                       const char *classname)
527 {
528   size_t classname_len;
529   char *p;
530   void *found;
531
532   if (classname[0] == '\0' || strchr (classname, ' ') != NULL)
533     /* Invalid classname argument.  */
534     abort ();
535
536   /* Push the classname onto the classname list.  */
537   classname_len = strlen (classname);
538   if (stream->curr_classes_length + 1 + classname_len + 1
539       > stream->curr_classes_allocated)
540     {
541       size_t new_allocated = stream->curr_classes_length + 1 + classname_len + 1;
542       if (new_allocated < 2 * stream->curr_classes_allocated)
543         new_allocated = 2 * stream->curr_classes_allocated;
544
545       stream->curr_classes = xrealloc (stream->curr_classes, new_allocated);
546       stream->curr_classes_allocated = new_allocated;
547     }
548   p = &stream->curr_classes[stream->curr_classes_length];
549   *p++ = ' ';
550   memcpy (p, classname, classname_len);
551   stream->curr_classes_length += 1 + classname_len;
552
553   /* Uodate stream->curr_attr.  */
554   if (hash_find_entry (&stream->cache,
555                        stream->curr_classes, stream->curr_classes_length,
556                        &found) < 0)
557     match_and_cache (stream);
558   else
559     stream->curr_attr = (attributes_t *) found;
560 }
561
562 static void
563 term_styled_ostream__end_use_class (term_styled_ostream_t stream,
564                                     const char *classname)
565 {
566   char *p_end;
567   char *p_start;
568   char *p;
569   void *found;
570
571   if (stream->curr_classes_length == 0)
572     /* No matching call to begin_use_class.  */
573     abort ();
574
575   /* Remove the trailing classname.  */
576   p_end = &stream->curr_classes[stream->curr_classes_length];
577   p = p_end;
578   while (*--p != ' ')
579     ;
580   p_start = p + 1;
581   if (!(p_end - p_start == strlen (classname)
582         && memcmp (p_start, classname, p_end - p_start) == 0))
583     /* The match ing call to begin_use_class used a different classname.  */
584     abort ();
585   stream->curr_classes_length = p - stream->curr_classes;
586
587   /* Update stream->curr_attr.  */
588   if (hash_find_entry (&stream->cache,
589                        stream->curr_classes, stream->curr_classes_length,
590                        &found) < 0)
591     abort ();
592   stream->curr_attr = (attributes_t *) found;
593 }
594
595 /* Constructor.  */
596
597 term_styled_ostream_t
598 term_styled_ostream_create (int fd, const char *filename,
599                             const char *css_filename)
600 {
601   term_styled_ostream_t stream =
602     XMALLOC (struct term_styled_ostream_representation);
603   CRStyleSheet *css_file_contents;
604
605   stream->base.base.vtable = &term_styled_ostream_vtable;
606   stream->destination = term_ostream_create (fd, filename);
607
608   if (cr_om_parser_simply_parse_file ((const guchar *) css_filename,
609                                       CR_UTF_8, /* CR_AUTO is not supported */
610                                       &css_file_contents) != CR_OK)
611     {
612       term_ostream_free (stream->destination);
613       free (stream);
614       return NULL;
615     }
616   stream->css_document = cr_cascade_new (NULL, css_file_contents, NULL);
617   stream->css_engine = cr_sel_eng_new ();
618
619   stream->curr_classes_allocated = 60;
620   stream->curr_classes = XNMALLOC (stream->curr_classes_allocated, char);
621   stream->curr_classes_length = 0;
622
623   hash_init (&stream->cache, 10);
624
625   match_and_cache (stream);
626
627   return stream;
628 }
629
630 #line 631 "term-styled-ostream.c"
631
632 const struct term_styled_ostream_implementation term_styled_ostream_vtable =
633 {
634   term_styled_ostream_superclasses,
635   sizeof (term_styled_ostream_superclasses) / sizeof (term_styled_ostream_superclasses[0]),
636   sizeof (struct term_styled_ostream_representation),
637   term_styled_ostream__write_mem,
638   term_styled_ostream__flush,
639   term_styled_ostream__free,
640   term_styled_ostream__begin_use_class,
641   term_styled_ostream__end_use_class,
642 };
643
644 #if !HAVE_INLINE
645
646 /* Define the functions that invoke the methods.  */
647
648 void
649 term_styled_ostream_write_mem (term_styled_ostream_t first_arg, const void *data, size_t len)
650 {
651   const struct term_styled_ostream_implementation *vtable =
652     ((struct term_styled_ostream_representation_header *) (struct term_styled_ostream_representation *) first_arg)->vtable;
653   vtable->write_mem (first_arg,data,len);
654 }
655
656 void
657 term_styled_ostream_flush (term_styled_ostream_t first_arg)
658 {
659   const struct term_styled_ostream_implementation *vtable =
660     ((struct term_styled_ostream_representation_header *) (struct term_styled_ostream_representation *) first_arg)->vtable;
661   vtable->flush (first_arg);
662 }
663
664 void
665 term_styled_ostream_free (term_styled_ostream_t first_arg)
666 {
667   const struct term_styled_ostream_implementation *vtable =
668     ((struct term_styled_ostream_representation_header *) (struct term_styled_ostream_representation *) first_arg)->vtable;
669   vtable->free (first_arg);
670 }
671
672 void
673 term_styled_ostream_begin_use_class (term_styled_ostream_t first_arg, const char *classname)
674 {
675   const struct term_styled_ostream_implementation *vtable =
676     ((struct term_styled_ostream_representation_header *) (struct term_styled_ostream_representation *) first_arg)->vtable;
677   vtable->begin_use_class (first_arg,classname);
678 }
679
680 void
681 term_styled_ostream_end_use_class (term_styled_ostream_t first_arg, const char *classname)
682 {
683   const struct term_styled_ostream_implementation *vtable =
684     ((struct term_styled_ostream_representation_header *) (struct term_styled_ostream_representation *) first_arg)->vtable;
685   vtable->end_use_class (first_arg,classname);
686 }
687
688 #endif