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 break;
221 }
222 important = FALSE;
223 cr_parser_try_to_skip_spaces_and_comments (parser);
224 status = cr_parser_parse_declaration (parser, &property,
225 &value, &important);
226 if (status != CR_OK || !property) {
227 if (status == CR_END_OF_INPUT_ERROR) {
228 status = CR_OK;
229 }
230 break;
231 }
232 cur_decl = cr_declaration_new (NULL, property, value);
233 if (cur_decl) {
234 cur_decl->important = important;
235 result = cr_declaration_append (result, cur_decl);
236 property = NULL;
237 value = NULL;
238 cur_decl = NULL;
239 } else {
240 break;
241 }
242 }
244 cleanup:
246 if (parser) {
247 cr_parser_destroy (parser);
248 parser = NULL;
249 }
251 if (property) {
252 cr_string_destroy (property);
253 property = NULL;
254 }
256 if (value) {
257 cr_term_destroy (value);
258 value = NULL;
259 }
261 if (status != CR_OK && result) {
262 cr_declaration_destroy (result);
263 result = NULL;
264 }
265 return result;
266 }
268 /**
269 *Appends a new declaration to the current declarations list.
270 *@param a_this the current declaration list.
271 *@param a_new the declaration to append.
272 *@return the declaration list with a_new appended to it, or NULL
273 *in case of error.
274 */
275 CRDeclaration *
276 cr_declaration_append (CRDeclaration * a_this, CRDeclaration * a_new)
277 {
278 CRDeclaration *cur = NULL;
280 g_return_val_if_fail (a_new, NULL);
282 if (!a_this)
283 return a_new;
285 for (cur = a_this; cur && cur->next; cur = cur->next) ;
287 cur->next = a_new;
288 a_new->prev = cur;
290 return a_this;
291 }
293 /**
294 *Unlinks the declaration from the declaration list.
295 *@param a_decl the declaration to unlink.
296 *@return a pointer to the unlinked declaration in
297 *case of a successfull completion, NULL otherwise.
298 */
299 CRDeclaration *
300 cr_declaration_unlink (CRDeclaration * a_decl)
301 {
302 CRDeclaration *result = a_decl;
304 g_return_val_if_fail (result, NULL);
306 /*
307 *some sanity checks first
308 */
309 if (a_decl->prev) {
310 g_return_val_if_fail (a_decl->prev->next == a_decl, NULL);
312 }
313 if (a_decl->next) {
314 g_return_val_if_fail (a_decl->next->prev == a_decl, NULL);
315 }
317 /*
318 *now, the real unlinking job.
319 */
320 if (a_decl->prev) {
321 a_decl->prev->next = a_decl->next;
322 }
323 if (a_decl->next) {
324 a_decl->next->prev = a_decl->prev;
325 }
326 if (a_decl->parent_statement) {
327 CRDeclaration **children_decl_ptr = NULL;
329 switch (a_decl->parent_statement->type) {
330 case RULESET_STMT:
331 if (a_decl->parent_statement->kind.ruleset) {
332 children_decl_ptr =
333 &a_decl->parent_statement->
334 kind.ruleset->decl_list;
335 }
337 break;
339 case AT_FONT_FACE_RULE_STMT:
340 if (a_decl->parent_statement->kind.font_face_rule) {
341 children_decl_ptr =
342 &a_decl->parent_statement->
343 kind.font_face_rule->decl_list;
344 }
345 break;
346 case AT_PAGE_RULE_STMT:
347 if (a_decl->parent_statement->kind.page_rule) {
348 children_decl_ptr =
349 &a_decl->parent_statement->
350 kind.page_rule->decl_list;
351 }
353 default:
354 break;
355 }
356 if (children_decl_ptr
357 && *children_decl_ptr && *children_decl_ptr == a_decl)
358 *children_decl_ptr = (*children_decl_ptr)->next;
359 }
361 a_decl->next = NULL;
362 a_decl->prev = NULL;
363 a_decl->parent_statement = NULL;
365 return result;
366 }
368 /**
369 *prepends a declaration to the current declaration list.
370 *@param a_this the current declaration list.
371 *@param a_new the declaration to prepend.
372 *@return the list with a_new prepended or NULL in case of error.
373 */
374 CRDeclaration *
375 cr_declaration_prepend (CRDeclaration * a_this, CRDeclaration * a_new)
376 {
377 CRDeclaration *cur = NULL;
379 g_return_val_if_fail (a_new, NULL);
381 if (!a_this)
382 return a_new;
384 a_this->prev = a_new;
385 a_new->next = a_this;
387 for (cur = a_new; cur && cur->prev; cur = cur->prev) ;
389 return cur;
390 }
392 /**
393 *Appends a declaration to the current declaration list.
394 *@param a_this the current declaration list.
395 *@param a_prop the property string of the declaration to append.
396 *@param a_value the value of the declaration to append.
397 *@return the list with the new property appended to it, or NULL in
398 *case of an error.
399 */
400 CRDeclaration *
401 cr_declaration_append2 (CRDeclaration * a_this,
402 CRString * a_prop, CRTerm * a_value)
403 {
404 CRDeclaration *new_elem = NULL;
406 if (a_this) {
407 new_elem = cr_declaration_new (a_this->parent_statement,
408 a_prop, a_value);
409 } else {
410 new_elem = cr_declaration_new (NULL, a_prop, a_value);
411 }
413 g_return_val_if_fail (new_elem, NULL);
415 return cr_declaration_append (a_this, new_elem);
416 }
418 /**
419 *Dumps a declaration list to a file.
420 *@param a_this the current instance of #CRDeclaration.
421 *@param a_fp the destination file.
422 *@param a_indent the number of indentation white char.
423 */
424 void
425 cr_declaration_dump (CRDeclaration * a_this, FILE * a_fp, glong a_indent,
426 gboolean a_one_per_line)
427 {
428 CRDeclaration *cur = NULL;
430 g_return_if_fail (a_this);
432 for (cur = a_this; cur; cur = cur->next) {
433 if (cur->prev) {
434 if (a_one_per_line == TRUE)
435 fprintf (a_fp, ";\n");
436 else
437 fprintf (a_fp, "; ");
438 }
439 dump (cur, a_fp, a_indent);
440 }
441 }
443 /**
444 *Dumps the first declaration of the declaration list to a file.
445 *@param a_this the current instance of #CRDeclaration.
446 *@param a_fp the destination file.
447 *@param a_indent the number of indentation white char.
448 */
449 void
450 cr_declaration_dump_one (CRDeclaration * a_this, FILE * a_fp, glong a_indent)
451 {
452 g_return_if_fail (a_this);
454 dump (a_this, a_fp, a_indent);
455 }
457 /**
458 *Serializes the declaration into a string
459 *@param a_this the current instance of #CRDeclaration.
460 *@param a_indent the number of indentation white char
461 *to put before the actual serialisation.
462 */
463 gchar *
464 cr_declaration_to_string (CRDeclaration * a_this, gulong a_indent)
465 {
466 GString *stringue = NULL;
468 gchar *str = NULL,
469 *result = NULL;
471 g_return_val_if_fail (a_this, NULL);
473 stringue = g_string_new (NULL);
475 if (a_this->property
476 && a_this->property->stryng
477 && a_this->property->stryng->str) {
478 str = g_strndup (a_this->property->stryng->str,
479 a_this->property->stryng->len);
480 if (str) {
481 cr_utils_dump_n_chars2 (' ', stringue,
482 a_indent);
483 g_string_append (stringue, str);
484 g_free (str);
485 str = NULL;
486 } else
487 goto error;
489 if (a_this->value) {
490 guchar *value_str = NULL;
492 value_str = cr_term_to_string (a_this->value);
493 if (value_str) {
494 g_string_append_printf (stringue, " : %s",
495 value_str);
496 g_free (value_str);
497 } else
498 goto error;
499 }
500 if (a_this->important == TRUE) {
501 g_string_append_printf (stringue, " %s",
502 "!important");
503 }
504 }
505 if (stringue && stringue->str) {
506 result = stringue->str;
507 g_string_free (stringue, FALSE);
508 }
509 return result;
511 error:
512 if (stringue) {
513 g_string_free (stringue, TRUE);
514 stringue = NULL;
515 }
516 if (str) {
517 g_free (str);
518 str = NULL;
519 }
521 return result;
522 }
524 /**
525 *Serializes the declaration list into a string
526 *@param a_this the current instance of #CRDeclaration.
527 *@param a_indent the number of indentation white char
528 *to put before the actual serialisation.
529 */
530 guchar *
531 cr_declaration_list_to_string (CRDeclaration * a_this, gulong a_indent)
532 {
533 CRDeclaration *cur = NULL;
534 GString *stringue = NULL;
535 gchar *str = NULL,
536 *result = NULL;
538 g_return_val_if_fail (a_this, NULL);
540 stringue = g_string_new (NULL);
542 for (cur = a_this; cur; cur = cur->next) {
543 str = cr_declaration_to_string (cur, a_indent);
544 if (str) {
545 g_string_append_printf (stringue, "%s;", str);
546 g_free (str);
547 } else
548 break;
549 }
550 if (stringue && stringue->str) {
551 result = stringue->str;
552 g_string_free (stringue, FALSE);
553 }
555 return (guchar *)result;
556 }
558 /**
559 *Serializes the declaration list into a string
560 *@param a_this the current instance of #CRDeclaration.
561 *@param a_indent the number of indentation white char
562 *to put before the actual serialisation.
563 */
564 guchar *
565 cr_declaration_list_to_string2 (CRDeclaration * a_this,
566 gulong a_indent, gboolean a_one_decl_per_line)
567 {
568 CRDeclaration *cur = NULL;
569 GString *stringue = NULL;
570 gchar *str = NULL,
571 *result = NULL;
573 g_return_val_if_fail (a_this, NULL);
575 stringue = g_string_new (NULL);
577 for (cur = a_this; cur; cur = cur->next) {
578 str = cr_declaration_to_string (cur, a_indent);
579 if (str) {
580 if (a_one_decl_per_line == TRUE) {
581 if (cur->next)
582 g_string_append_printf (stringue,
583 "%s;\n", str);
584 else
585 g_string_append (stringue,
586 str);
587 } else {
588 if (cur->next)
589 g_string_append_printf (stringue,
590 "%s;", str);
591 else
592 g_string_append (stringue,
593 str);
594 }
595 g_free (str);
596 } else
597 break;
598 }
599 if (stringue && stringue->str) {
600 result = stringue->str;
601 g_string_free (stringue, FALSE);
602 }
604 return (guchar *)result;
605 }
607 /**
608 *Return the number of properties in the declaration;
609 *@param a_this the current instance of #CRDeclaration.
610 *@return number of properties in the declaration list.
611 */
612 gint
613 cr_declaration_nr_props (CRDeclaration * a_this)
614 {
615 CRDeclaration *cur = NULL;
616 int nr = 0;
618 g_return_val_if_fail (a_this, -1);
620 for (cur = a_this; cur; cur = cur->next)
621 nr++;
622 return nr;
623 }
625 /**
626 *Use an index to get a CRDeclaration from the declaration list.
627 *@param a_this the current instance of #CRDeclaration.
628 *@param itemnr the index into the declaration list.
629 *@return CRDeclaration at position itemnr, if itemnr > number of declarations - 1,
630 *it will return NULL.
631 */
632 CRDeclaration *
633 cr_declaration_get_from_list (CRDeclaration * a_this, int itemnr)
634 {
635 CRDeclaration *cur = NULL;
636 int nr = 0;
638 g_return_val_if_fail (a_this, NULL);
640 for (cur = a_this; cur; cur = cur->next)
641 if (nr++ == itemnr)
642 return cur;
643 return NULL;
644 }
646 /**
647 *Use property name to get a CRDeclaration from the declaration list.
648 *@param a_this the current instance of #CRDeclaration.
649 *@param a_prop the property name to search for.
650 *@return CRDeclaration with property name a_prop, or NULL if not found.
651 */
652 CRDeclaration *
653 cr_declaration_get_by_prop_name (CRDeclaration * a_this,
654 const guchar * a_prop)
655 {
656 CRDeclaration *cur = NULL;
658 g_return_val_if_fail (a_this, NULL);
659 g_return_val_if_fail (a_prop, NULL);
661 for (cur = a_this; cur; cur = cur->next) {
662 if (cur->property
663 && cur->property->stryng
664 && cur->property->stryng->str) {
665 if (!strcmp (cur->property->stryng->str,
666 (char *)a_prop)) {
667 return cur;
668 }
669 }
670 }
671 return NULL;
672 }
674 /**
675 *Increases the ref count of the current instance of #CRDeclaration.
676 *@param a_this the current instance of #CRDeclaration.
677 */
678 void
679 cr_declaration_ref (CRDeclaration * a_this)
680 {
681 g_return_if_fail (a_this);
683 a_this->ref_count++;
684 }
686 /**
687 *Decrements the ref count of the current instance of #CRDeclaration.
688 *If the ref count reaches zero, the current instance of #CRDeclaration
689 *if destroyed.
690 *@param a_this the current instance of #CRDeclaration.
691 *@return TRUE if the current instance of #CRDeclaration has been destroyed
692 *(ref count reached zero), FALSE otherwise.
693 */
694 gboolean
695 cr_declaration_unref (CRDeclaration * a_this)
696 {
697 g_return_val_if_fail (a_this, FALSE);
699 if (a_this->ref_count) {
700 a_this->ref_count--;
701 }
703 if (a_this->ref_count == 0) {
704 cr_declaration_destroy (a_this);
705 return TRUE;
706 }
707 return FALSE;
708 }
710 /**
711 *Destructor of the declaration list.
712 *@param a_this the current instance of #CRDeclaration.
713 */
714 void
715 cr_declaration_destroy (CRDeclaration * a_this)
716 {
717 CRDeclaration *cur = NULL;
719 g_return_if_fail (a_this);
721 /*
722 *Go get the tail of the list.
723 *Meanwhile, free each property/value pair contained in the list.
724 */
725 for (cur = a_this; cur && cur->next; cur = cur->next) {
726 if (cur->property) {
727 cr_string_destroy (cur->property);
728 cur->property = NULL;
729 }
731 if (cur->value) {
732 cr_term_destroy (cur->value);
733 cur->value = NULL;
734 }
735 }
737 if (cur) {
738 if (cur->property) {
739 cr_string_destroy (cur->property);
740 cur->property = NULL;
741 }
743 if (cur->value) {
744 cr_term_destroy (cur->value);
745 cur->value = NULL;
746 }
747 }
749 /*in case the list contains only one element */
750 if (cur && !cur->prev) {
751 g_free (cur);
752 return;
753 }
755 /*walk backward the list and free each "next" element */
756 for (cur = cur->prev; cur && cur->prev; cur = cur->prev) {
757 if (cur->next) {
758 g_free (cur->next);
759 cur->next = NULL;
760 }
761 }
763 if (!cur)
764 return;
766 if (cur->next) {
767 g_free (cur->next);
768 cur->next = NULL;
769 }
771 g_free (cur);
772 }