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)
109 {
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;
159 }
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)
171 {
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;
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 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;
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 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;
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 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;
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 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 }