Code

User message context in extensions
[inkscape.git] / src / libcroco / cr-declaration.c
1 /* -*- Mode: C; indent-tabs-mode: ni; c-basic-offset: 8 -*- */
3 /*
4  * This file is part of The Croco Library
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of version 2.1 of the GNU Lesser General Public
8  * License as published by the Free Software Foundation.
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 Lesser General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18  * USA
19  *
20  * Author: Dodji Seketeli.
21  * See COPYRIGHTS file for copyright information.
22  */
25 #include <string.h>
26 #include "cr-declaration.h"
27 #include "cr-statement.h"
28 #include "cr-parser.h"
30 /**
31  *@file
32  *The definition of the #CRDeclaration class.
33  */
35 /**
36  *Dumps (serializes) one css declaration to a file.
37  *@param a_this the current instance of #CRDeclaration.
38  *@param a_fp the destination file pointer.
39  *@param a_indent the number of indentation white char. 
40  */
41 static void
42 dump (CRDeclaration * a_this, FILE * a_fp, glong a_indent)
43 {
44         g_return_if_fail (a_this);
46         gchar *str = cr_declaration_to_string (a_this, a_indent);
47         if (str) {
48                 fprintf (a_fp, "%s", str);
49                 g_free (str);
50                 str = NULL;
51         }
52 }
54 /**
55  *Constructor of #CRDeclaration.
56  *@param a_property the property string of the declaration
57  *@param a_value the value expression of the declaration.
58  *@return the newly built instance of #CRDeclaration, or NULL in
59  *case of error.
60  */
61 CRDeclaration *
62 cr_declaration_new (CRStatement * a_statement,
63                     CRString * a_property, CRTerm * a_value)
64 {
65         g_return_val_if_fail (a_property, NULL);
67         if (a_statement)
68                 g_return_val_if_fail (a_statement
69                                       && ((a_statement->type == RULESET_STMT)
70                                           || (a_statement->type
71                                               == AT_FONT_FACE_RULE_STMT)
72                                           || (a_statement->type
73                                               == AT_PAGE_RULE_STMT)), NULL);
75         CRDeclaration * result = (CRDeclaration *)g_try_malloc (sizeof (CRDeclaration));
76         if (!result) {
77                 cr_utils_trace_info ("Out of memory");
78                 return NULL;
79         }
80         memset (result, 0, sizeof (CRDeclaration));
81         result->property = a_property;
82         result->value = a_value;
84         if (a_value) {
85                 cr_term_ref (a_value);
86         }
87         result->parent_statement = a_statement;
88         return result;
89 }
91 /**
92  *Parses a text buffer that contains
93  *a css declaration.
94  *
95  *@param a_statement the parent css2 statement of this
96  *this declaration. Must be non NULL and of type
97  *RULESET_STMT (must be a ruleset).
98  *@param a_str the string that contains the statement.
99  *@param a_enc the encoding of a_str.
100  *@return the parsed declaration, or NULL in case of error.
101  */
102 CRDeclaration *
103 cr_declaration_parse_from_buf (CRStatement * a_statement,
104                                const guchar * a_str, enum CREncoding a_enc)
106         enum CRStatus status = CR_OK;
107         CRTerm *value = NULL;
108         CRString *property = NULL;
109         CRDeclaration *result = NULL;
110         gboolean important = FALSE;
112         g_return_val_if_fail (a_str, NULL);
113         if (a_statement)
114                 g_return_val_if_fail (a_statement->type == RULESET_STMT,
115                                       NULL);
117         CRParser *parser = (CRParser *)
118                         cr_parser_new_from_buf ((guchar*)a_str,
119                                   strlen ((char *)a_str), a_enc, FALSE);
120         g_return_val_if_fail (parser, NULL);
122         status = cr_parser_try_to_skip_spaces_and_comments (parser);
123         if (status != CR_OK)
124                 goto cleanup;
126         status = cr_parser_parse_declaration (parser, &property,
127                                               &value, &important);
128         if (status != CR_OK || !property)
129                 goto cleanup;
131         result = cr_declaration_new (a_statement, property, value);
132         if (result) {
133                 property = NULL;
134                 value = NULL;
135                 result->important = important;
136         }
138       cleanup:
140         if (parser) {
141                 cr_parser_destroy (parser);
142                 parser = NULL;
143         }
145         if (property) {
146                 cr_string_destroy (property);
147                 property = NULL;
148         }
150         if (value) {
151                 cr_term_destroy (value);
152                 value = NULL;
153         }
155         return result;
158 /**
159  *Parses a ';' separated list of properties declaration.
160  *@param a_str the input buffer that contains the list of declaration to
161  *parse.
162  *@param a_enc the encoding of a_str
163  *@return the parsed list of declaration, NULL if parsing failed.
164  */
165 CRDeclaration *
166 cr_declaration_parse_list_from_buf (const guchar * a_str,
167                                     enum CREncoding a_enc)
170         enum CRStatus status = CR_OK;
171         CRTerm *value = NULL;
172         CRString *property = NULL;
173         CRDeclaration *result = NULL,
174                 *cur_decl = NULL;
175         CRTknzr *tokenizer = NULL;
176         gboolean important = FALSE;
178         g_return_val_if_fail (a_str, NULL);
180         CRParser *parser = (CRParser *)cr_parser_new_from_buf
181                       ((guchar*)a_str, strlen ((char *)a_str), a_enc, FALSE);
182         g_return_val_if_fail (parser, NULL);
183         status = cr_parser_get_tknzr (parser, &tokenizer);
184         if (status != CR_OK || !tokenizer) {
185                 if (status == CR_OK)
186                         status = CR_ERROR;
187                 goto cleanup;
188         }
189         status = cr_parser_try_to_skip_spaces_and_comments (parser);
190         if (status != CR_OK)
191                 goto cleanup;
193         status = cr_parser_parse_declaration (parser, &property,
194                                               &value, &important);
195         if (status != CR_OK || !property) {
196                 if (status != CR_OK)
197                         status = CR_ERROR;
198                 goto cleanup;
199         }
200         result = cr_declaration_new (NULL, property, value);
201         if (result) {
202                 property = NULL;
203                 value = NULL;
204                 result->important = important;
205         }
206         /*now, go parse the other declarations */
207         for (;;) {
208                 guint32 c = 0;
210                 cr_parser_try_to_skip_spaces_and_comments (parser);
211                 status = cr_tknzr_peek_char (tokenizer, &c);
212                 if (status != CR_OK) {
213                         if (status == CR_END_OF_INPUT_ERROR) 
214                                 status = CR_OK;
215                         goto cleanup;
216                 }
217                 if (c == ';') {
218                         status = cr_tknzr_read_char (tokenizer, &c);
219                 } else {
220                         cr_tknzr_read_char (tokenizer, &c);
221                         continue; // try to keep reading until we reach the end or a ;
222                 }
223                 important = FALSE;
224                 cr_parser_try_to_skip_spaces_and_comments (parser);
225                 status = cr_parser_parse_declaration (parser, &property,
226                                                       &value, &important);
227                 if (status != CR_OK || !property) {
228                         if (status == CR_END_OF_INPUT_ERROR) {
229                                 status = CR_OK; // simply the end of input, do not delete what we got so far, just finish
230                                 break; 
231                         } else {
232                                 continue; // even if one declaration is broken, it's no reason to discard others (see http://www.w3.org/TR/CSS21/syndata.html#declaration)
233                                                                 }
234                 }
235                 cur_decl = cr_declaration_new (NULL, property, value);
236                 if (cur_decl) {
237                         cur_decl->important = important;
238                         result = cr_declaration_append (result, cur_decl);
239                         property = NULL;
240                         value = NULL;
241                         cur_decl = NULL;
242                 } else {
243                         break;
244                 }
245         }
247       cleanup:
249         if (parser) {
250                 cr_parser_destroy (parser);
251                 parser = NULL;
252         }
254         if (property) {
255                 cr_string_destroy (property);
256                 property = NULL;
257         }
259         if (value) {
260                 cr_term_destroy (value);
261                 value = NULL;
262         }
264         if (status != CR_OK && result) {
265                 cr_declaration_destroy (result);
266                 result = NULL;
267         }
268         return result;
271 /**
272  *Appends a new declaration to the current declarations list.
273  *@param a_this the current declaration list.
274  *@param a_new the declaration to append.
275  *@return the declaration list with a_new appended to it, or NULL
276  *in case of error.
277  */
278 CRDeclaration *
279 cr_declaration_append (CRDeclaration * a_this, CRDeclaration * a_new)
281         CRDeclaration *cur = NULL;
283         g_return_val_if_fail (a_new, NULL);
285         if (!a_this)
286                 return a_new;
288         for (cur = a_this; cur && cur->next; cur = cur->next) ;
290         cur->next = a_new;
291         a_new->prev = cur;
293         return a_this;
296 /**
297  *Unlinks the declaration from the declaration list.
298  *@param a_decl the declaration to unlink.
299  *@return a pointer to the unlinked declaration in
300  *case of a successfull completion, NULL otherwise.
301  */
302 CRDeclaration *
303 cr_declaration_unlink (CRDeclaration * a_decl)
305         CRDeclaration *result = a_decl;
307         g_return_val_if_fail (result, NULL);
309         /*
310          *some sanity checks first
311          */
312         if (a_decl->prev) {
313                 g_return_val_if_fail (a_decl->prev->next == a_decl, NULL);
315         }
316         if (a_decl->next) {
317                 g_return_val_if_fail (a_decl->next->prev == a_decl, NULL);
318         }
320         /*
321          *now, the real unlinking job.
322          */
323         if (a_decl->prev) {
324                 a_decl->prev->next = a_decl->next;
325         }
326         if (a_decl->next) {
327                 a_decl->next->prev = a_decl->prev;
328         }
329         if (a_decl->parent_statement) {
330                 CRDeclaration **children_decl_ptr = NULL;
332                 switch (a_decl->parent_statement->type) {
333                 case RULESET_STMT:
334                         if (a_decl->parent_statement->kind.ruleset) {
335                                 children_decl_ptr =
336                                         &a_decl->parent_statement->
337                                         kind.ruleset->decl_list;
338                         }
340                         break;
342                 case AT_FONT_FACE_RULE_STMT:
343                         if (a_decl->parent_statement->kind.font_face_rule) {
344                                 children_decl_ptr =
345                                         &a_decl->parent_statement->
346                                         kind.font_face_rule->decl_list;
347                         }
348                         break;
349                 case AT_PAGE_RULE_STMT:
350                         if (a_decl->parent_statement->kind.page_rule) {
351                                 children_decl_ptr =
352                                         &a_decl->parent_statement->
353                                         kind.page_rule->decl_list;
354                         }
356                 default:
357                         break;
358                 }
359                 if (children_decl_ptr
360                     && *children_decl_ptr && *children_decl_ptr == a_decl)
361                         *children_decl_ptr = (*children_decl_ptr)->next;
362         }
364         a_decl->next = NULL;
365         a_decl->prev = NULL;
366         a_decl->parent_statement = NULL;
368         return result;
371 /**
372  *prepends a declaration to the current declaration list.
373  *@param a_this the current declaration list.
374  *@param a_new the declaration to prepend.
375  *@return the list with a_new prepended or NULL in case of error.
376  */
377 CRDeclaration *
378 cr_declaration_prepend (CRDeclaration * a_this, CRDeclaration * a_new)
380         CRDeclaration *cur = NULL;
382         g_return_val_if_fail (a_new, NULL);
384         if (!a_this)
385                 return a_new;
387         a_this->prev = a_new;
388         a_new->next = a_this;
390         for (cur = a_new; cur && cur->prev; cur = cur->prev) ;
392         return cur;
395 /**
396  *Appends a declaration to the current declaration list.
397  *@param a_this the current declaration list.
398  *@param a_prop the property string of the declaration to append.
399  *@param a_value the value of the declaration to append.
400  *@return the list with the new property appended to it, or NULL in
401  *case of an error.
402  */
403 CRDeclaration *
404 cr_declaration_append2 (CRDeclaration * a_this,
405                         CRString * a_prop, CRTerm * a_value)
407         CRDeclaration *new_elem = NULL;
409         if (a_this) {
410                 new_elem = cr_declaration_new (a_this->parent_statement,
411                                                a_prop, a_value);
412         } else {
413                 new_elem = cr_declaration_new (NULL, a_prop, a_value);
414         }
416         g_return_val_if_fail (new_elem, NULL);
418         return cr_declaration_append (a_this, new_elem);
421 /**
422  *Dumps a declaration list to a file.
423  *@param a_this the current instance of #CRDeclaration.
424  *@param a_fp the destination file.
425  *@param a_indent the number of indentation white char.
426  */
427 void
428 cr_declaration_dump (CRDeclaration * a_this, FILE * a_fp, glong a_indent,
429                      gboolean a_one_per_line)
431         CRDeclaration *cur = NULL;
433         g_return_if_fail (a_this);
435         for (cur = a_this; cur; cur = cur->next) {
436                 if (cur->prev) {
437                         if (a_one_per_line == TRUE)
438                                 fprintf (a_fp, ";\n");
439                         else
440                                 fprintf (a_fp, "; ");
441                 }
442                 dump (cur, a_fp, a_indent);
443         }
446 /**
447  *Dumps the first declaration of the declaration list to a file.
448  *@param a_this the current instance of #CRDeclaration.
449  *@param a_fp the destination file.
450  *@param a_indent the number of indentation white char.
451  */
452 void
453 cr_declaration_dump_one (CRDeclaration * a_this, FILE * a_fp, glong a_indent)
455         g_return_if_fail (a_this);
457         dump (a_this, a_fp, a_indent);
460 /**
461  *Serializes the declaration into a string
462  *@param a_this the current instance of #CRDeclaration.
463  *@param a_indent the number of indentation white char
464  *to put before the actual serialisation.
465  */
466 gchar *
467 cr_declaration_to_string (CRDeclaration * a_this, gulong a_indent)
469         GString *stringue = NULL;
471         gchar *str = NULL,
472                 *result = NULL;
474         g_return_val_if_fail (a_this, NULL);
476         stringue = g_string_new (NULL);
478         if (a_this->property 
479             && a_this->property->stryng
480             && a_this->property->stryng->str) {
481                 str = g_strndup (a_this->property->stryng->str,
482                                  a_this->property->stryng->len);
483                 if (str) {
484                         cr_utils_dump_n_chars2 (' ', stringue, 
485                                                 a_indent);
486                         g_string_append (stringue, str);
487                         g_free (str);
488                         str = NULL;
489                 } else
490                         goto error;
492                 if (a_this->value) {
493                         guchar *value_str = NULL;
495                         value_str = cr_term_to_string (a_this->value);
496                         if (value_str) {
497                                 g_string_append_printf (stringue, " : %s",
498                                                         value_str);
499                                 g_free (value_str);
500                         } else
501                                 goto error;
502                 }
503                 if (a_this->important == TRUE) {
504                         g_string_append_printf (stringue, " %s",
505                                                 "!important");
506                 }
507         }
508         if (stringue && stringue->str) {
509                 result = stringue->str;
510                 g_string_free (stringue, FALSE);
511         }
512         return result;
514       error:
515         if (stringue) {
516                 g_string_free (stringue, TRUE);
517                 stringue = NULL;
518         }
519         if (str) {
520                 g_free (str);
521                 str = NULL;
522         }
524         return result;
527 /**
528  *Serializes the declaration list into a string
529  *@param a_this the current instance of #CRDeclaration.
530  *@param a_indent the number of indentation white char
531  *to put before the actual serialisation.
532  */
533 guchar *
534 cr_declaration_list_to_string (CRDeclaration * a_this, gulong a_indent)
536         CRDeclaration *cur = NULL;
537         GString *stringue = NULL;
538         gchar *str = NULL,
539                 *result = NULL;
541         g_return_val_if_fail (a_this, NULL);
543         stringue = g_string_new (NULL);
545         for (cur = a_this; cur; cur = cur->next) {
546                 str = cr_declaration_to_string (cur, a_indent);
547                 if (str) {
548                         g_string_append_printf (stringue, "%s;", str);
549                         g_free (str);
550                 } else
551                         break;
552         }
553         if (stringue && stringue->str) {
554                 result = stringue->str;
555                 g_string_free (stringue, FALSE);
556         }
558         return (guchar *)result;
561 /**
562  *Serializes the declaration list into a string
563  *@param a_this the current instance of #CRDeclaration.
564  *@param a_indent the number of indentation white char
565  *to put before the actual serialisation.
566  */
567 guchar *
568 cr_declaration_list_to_string2 (CRDeclaration * a_this,
569                                 gulong a_indent, gboolean a_one_decl_per_line)
571         CRDeclaration *cur = NULL;
572         GString *stringue = NULL;
573         gchar *str = NULL,
574                 *result = NULL;
576         g_return_val_if_fail (a_this, NULL);
578         stringue = g_string_new (NULL);
580         for (cur = a_this; cur; cur = cur->next) {
581                 str = cr_declaration_to_string (cur, a_indent);
582                 if (str) {
583                         if (a_one_decl_per_line == TRUE) {
584                                 if (cur->next)
585                                         g_string_append_printf (stringue,
586                                                                 "%s;\n", str);
587                                 else
588                                         g_string_append (stringue,
589                                                          str);
590                         } else {
591                                 if (cur->next)
592                                         g_string_append_printf (stringue,
593                                                                 "%s;", str);
594                                 else
595                                         g_string_append (stringue,
596                                                          str);
597                         }
598                         g_free (str);
599                 } else
600                         break;
601         }
602         if (stringue && stringue->str) {
603                 result = stringue->str;
604                 g_string_free (stringue, FALSE);
605         }
607         return (guchar *)result;
610 /**
611  *Return the number of properties in the declaration;
612  *@param a_this the current instance of #CRDeclaration.
613  *@return number of properties in the declaration list.
614  */
615 gint
616 cr_declaration_nr_props (CRDeclaration * a_this)
618         CRDeclaration *cur = NULL;
619         int nr = 0;
621         g_return_val_if_fail (a_this, -1);
623         for (cur = a_this; cur; cur = cur->next)
624                 nr++;
625         return nr;
628 /**
629  *Use an index to get a CRDeclaration from the declaration list.
630  *@param a_this the current instance of #CRDeclaration.
631  *@param itemnr the index into the declaration list.
632  *@return CRDeclaration at position itemnr, if itemnr > number of declarations - 1,
633  *it will return NULL.
634  */
635 CRDeclaration *
636 cr_declaration_get_from_list (CRDeclaration * a_this, int itemnr)
638         CRDeclaration *cur = NULL;
639         int nr = 0;
641         g_return_val_if_fail (a_this, NULL);
643         for (cur = a_this; cur; cur = cur->next)
644                 if (nr++ == itemnr)
645                         return cur;
646         return NULL;
649 /**
650  *Use property name to get a CRDeclaration from the declaration list.
651  *@param a_this the current instance of #CRDeclaration.
652  *@param a_prop the property name to search for.
653  *@return CRDeclaration with property name a_prop, or NULL if not found.
654  */
655 CRDeclaration *
656 cr_declaration_get_by_prop_name (CRDeclaration * a_this,
657                                  const guchar * a_prop)
659         CRDeclaration *cur = NULL;
661         g_return_val_if_fail (a_this, NULL);
662         g_return_val_if_fail (a_prop, NULL);
664         for (cur = a_this; cur; cur = cur->next) {
665                 if (cur->property 
666                     && cur->property->stryng
667                     && cur->property->stryng->str) {
668                         if (!strcmp (cur->property->stryng->str, 
669                                      (char *)a_prop)) {
670                                 return cur;
671                         }
672                 }
673         }
674         return NULL;
677 /**
678  *Increases the ref count of the current instance of #CRDeclaration.
679  *@param a_this the current instance of #CRDeclaration.
680  */
681 void
682 cr_declaration_ref (CRDeclaration * a_this)
684         g_return_if_fail (a_this);
686         a_this->ref_count++;
689 /**
690  *Decrements the ref count of the current instance of #CRDeclaration.
691  *If the ref count reaches zero, the current instance of #CRDeclaration
692  *if destroyed.
693  *@param a_this the current instance of #CRDeclaration.
694  *@return TRUE if the current instance of #CRDeclaration has been destroyed
695  *(ref count reached zero), FALSE otherwise.
696  */
697 gboolean
698 cr_declaration_unref (CRDeclaration * a_this)
700         g_return_val_if_fail (a_this, FALSE);
702         if (a_this->ref_count) {
703                 a_this->ref_count--;
704         }
706         if (a_this->ref_count == 0) {
707                 cr_declaration_destroy (a_this);
708                 return TRUE;
709         }
710         return FALSE;
713 /**
714  *Destructor of the declaration list.
715  *@param a_this the current instance of #CRDeclaration.
716  */
717 void
718 cr_declaration_destroy (CRDeclaration * a_this)
720         CRDeclaration *cur = NULL;
722         g_return_if_fail (a_this);
724         /*
725          *Go get the tail of the list.
726          *Meanwhile, free each property/value pair contained in the list.
727          */
728         for (cur = a_this; cur && cur->next; cur = cur->next) {
729                 if (cur->property) {
730                         cr_string_destroy (cur->property);
731                         cur->property = NULL;
732                 }
734                 if (cur->value) {
735                         cr_term_destroy (cur->value);
736                         cur->value = NULL;
737                 }
738         }
740         if (cur) {
741                 if (cur->property) {
742                         cr_string_destroy (cur->property);
743                         cur->property = NULL;
744                 }
746                 if (cur->value) {
747                         cr_term_destroy (cur->value);
748                         cur->value = NULL;
749                 }
750         }
752         /*in case the list contains only one element */
753         if (cur && !cur->prev) {
754                 g_free (cur);
755                 return;
756         }
758         /*walk backward the list and free each "next" element */
759         for (cur = cur->prev; cur && cur->prev; cur = cur->prev) {
760                 if (cur->next) {
761                         g_free (cur->next);
762                         cur->next = NULL;
763                 }
764         }
766         if (!cur)
767                 return;
769         if (cur->next) {
770                 g_free (cur->next);
771                 cur->next = NULL;
772         }
774         g_free (cur);