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)
105 {
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;
156 }
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)
168 {
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;
269 }
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)
280 {
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;
294 }
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)
304 {
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;
369 }
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)
379 {
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;
393 }
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)
406 {
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);
419 }
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)
430 {
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 }
444 }
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)
454 {
455 g_return_if_fail (a_this);
457 dump (a_this, a_fp, a_indent);
458 }
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)
468 {
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;
525 }
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)
535 {
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;
559 }
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)
570 {
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;
608 }
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)
617 {
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;
626 }
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)
637 {
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;
647 }
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)
658 {
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;
675 }
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)
683 {
684 g_return_if_fail (a_this);
686 a_this->ref_count++;
687 }
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)
699 {
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;
711 }
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)
719 {
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);
775 }