Code

a1ff0d292335faa86f914fbd9c7eb60ab8af58b4
[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         guchar *str = NULL;
46         g_return_if_fail (a_this);
48         str = cr_declaration_to_string (a_this, a_indent);
49         if (str) {
50                 fprintf (a_fp, "%s", str);
51                 g_free (str);
52                 str = NULL;
53         }
54 }
56 /**
57  *Constructor of #CRDeclaration.
58  *@param a_property the property string of the declaration
59  *@param a_value the value expression of the declaration.
60  *@return the newly built instance of #CRDeclaration, or NULL in
61  *case of error.
62  */
63 CRDeclaration *
64 cr_declaration_new (CRStatement * a_statement,
65                     CRString * a_property, CRTerm * a_value)
66 {
67         CRDeclaration *result = NULL;
69         g_return_val_if_fail (a_property, NULL);
71         if (a_statement)
72                 g_return_val_if_fail (a_statement
73                                       && ((a_statement->type == RULESET_STMT)
74                                           || (a_statement->type
75                                               == AT_FONT_FACE_RULE_STMT)
76                                           || (a_statement->type
77                                               == AT_PAGE_RULE_STMT)), NULL);
79         result = g_try_malloc (sizeof (CRDeclaration));
80         if (!result) {
81                 cr_utils_trace_info ("Out of memory");
82                 return NULL;
83         }
84         memset (result, 0, sizeof (CRDeclaration));
85         result->property = a_property;
86         result->value = a_value;
88         if (a_value) {
89                 cr_term_ref (a_value);
90         }
91         result->parent_statement = a_statement;
92         return result;
93 }
95 /**
96  *Parses a text buffer that contains
97  *a css declaration.
98  *
99  *@param a_statement the parent css2 statement of this
100  *this declaration. Must be non NULL and of type
101  *RULESET_STMT (must be a ruleset).
102  *@param a_str the string that contains the statement.
103  *@param a_enc the encoding of a_str.
104  *@return the parsed declaration, or NULL in case of error.
105  */
106 CRDeclaration *
107 cr_declaration_parse_from_buf (CRStatement * a_statement,
108                                const guchar * a_str, enum CREncoding a_enc)
110         enum CRStatus status = CR_OK;
111         CRTerm *value = NULL;
112         CRString *property = NULL;
113         CRDeclaration *result = NULL;
114         CRParser *parser = NULL;
115         gboolean important = FALSE;
117         g_return_val_if_fail (a_str, NULL);
118         if (a_statement)
119                 g_return_val_if_fail (a_statement->type == RULESET_STMT,
120                                       NULL);
122         parser = cr_parser_new_from_buf ((guchar*)a_str, strlen (a_str), a_enc, FALSE);
123         g_return_val_if_fail (parser, NULL);
125         status = cr_parser_try_to_skip_spaces_and_comments (parser);
126         if (status != CR_OK)
127                 goto cleanup;
129         status = cr_parser_parse_declaration (parser, &property,
130                                               &value, &important);
131         if (status != CR_OK || !property)
132                 goto cleanup;
134         result = cr_declaration_new (a_statement, property, value);
135         if (result) {
136                 property = NULL;
137                 value = NULL;
138                 result->important = important;
139         }
141       cleanup:
143         if (parser) {
144                 cr_parser_destroy (parser);
145                 parser = NULL;
146         }
148         if (property) {
149                 cr_string_destroy (property);
150                 property = NULL;
151         }
153         if (value) {
154                 cr_term_destroy (value);
155                 value = NULL;
156         }
158         return result;
161 /**
162  *Parses a ';' separated list of properties declaration.
163  *@param a_str the input buffer that contains the list of declaration to
164  *parse.
165  *@param a_enc the encoding of a_str
166  *@return the parsed list of declaration, NULL if parsing failed.
167  */
168 CRDeclaration *
169 cr_declaration_parse_list_from_buf (const guchar * a_str,
170                                     enum CREncoding a_enc)
173         enum CRStatus status = CR_OK;
174         CRTerm *value = NULL;
175         CRString *property = NULL;
176         CRDeclaration *result = NULL,
177                 *cur_decl = NULL;
178         CRParser *parser = NULL;
179         CRTknzr *tokenizer = NULL;
180         gboolean important = FALSE;
182         g_return_val_if_fail (a_str, NULL);
184         parser = cr_parser_new_from_buf ((guchar*)a_str, strlen (a_str), a_enc, FALSE);
185         g_return_val_if_fail (parser, NULL);
186         status = cr_parser_get_tknzr (parser, &tokenizer);
187         if (status != CR_OK || !tokenizer) {
188                 if (status == CR_OK)
189                         status = CR_ERROR;
190                 goto cleanup;
191         }
192         status = cr_parser_try_to_skip_spaces_and_comments (parser);
193         if (status != CR_OK)
194                 goto cleanup;
196         status = cr_parser_parse_declaration (parser, &property,
197                                               &value, &important);
198         if (status != CR_OK || !property) {
199                 if (status != CR_OK)
200                         status = CR_ERROR;
201                 goto cleanup;
202         }
203         result = cr_declaration_new (NULL, property, value);
204         if (result) {
205                 property = NULL;
206                 value = NULL;
207                 result->important = important;
208         }
209         /*now, go parse the other declarations */
210         for (;;) {
211                 guint32 c = 0;
213                 cr_parser_try_to_skip_spaces_and_comments (parser);
214                 status = cr_tknzr_peek_char (tokenizer, &c);
215                 if (status != CR_OK) {
216                         if (status == CR_END_OF_INPUT_ERROR)
217                                 status = CR_OK;
218                         goto cleanup;
219                 }
220                 if (c == ';') {
221                         status = cr_tknzr_read_char (tokenizer, &c);
222                 } else {
223                         break;
224                 }
225                 important = FALSE;
226                 cr_parser_try_to_skip_spaces_and_comments (parser);
227                 status = cr_parser_parse_declaration (parser, &property,
228                                                       &value, &important);
229                 if (status != CR_OK || !property) {
230                         if (status == CR_END_OF_INPUT_ERROR) {
231                                 status = CR_OK;
232                         }
233                         break;
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         guchar *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         guchar *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 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         guchar *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 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                                      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);