Imported Upstream version 0.18.1.1
[platform/upstream/gettext.git] / gnulib-local / lib / libcroco / cr-declaration.c
1 /* -*- Mode: C; indent-tabs-mode: ni; c-basic-offset: 8 -*- */
2
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  */
23
24 #include <config.h>
25 #include <string.h>
26 #include "cr-declaration.h"
27 #include "cr-statement.h"
28 #include "cr-parser.h"
29
30 /**
31  *@CRDeclaration:
32  *
33  *The definition of the #CRDeclaration class.
34  */
35
36 /**
37  * dump:
38  *@a_this: the current instance of #CRDeclaration.
39  *@a_fp: the destination file pointer.
40  *@a_indent: the number of indentation white char. 
41  *
42  *Dumps (serializes) one css declaration to a file.
43  */
44 static void
45 dump (CRDeclaration * a_this, FILE * a_fp, glong a_indent)
46 {
47         guchar *str = NULL;
48
49         g_return_if_fail (a_this);
50
51         str = cr_declaration_to_string (a_this, a_indent);
52         if (str) {
53                 fprintf (a_fp, "%s", str);
54                 g_free (str);
55                 str = NULL;
56         }
57 }
58
59 /**
60  * cr_declaration_new:
61  * @a_statement: the statement this declaration belongs to. can be NULL.
62  *@a_property: the property string of the declaration
63  *@a_value: the value expression of the declaration.
64  *Constructor of #CRDeclaration.
65  *
66  *Returns the newly built instance of #CRDeclaration, or NULL in
67  *case of error.
68  */
69 CRDeclaration *
70 cr_declaration_new (CRStatement * a_statement,
71                     CRString * a_property, CRTerm * a_value)
72 {
73         CRDeclaration *result = NULL;
74
75         g_return_val_if_fail (a_property, NULL);
76
77         if (a_statement)
78                 g_return_val_if_fail (a_statement
79                                       && ((a_statement->type == RULESET_STMT)
80                                           || (a_statement->type
81                                               == AT_FONT_FACE_RULE_STMT)
82                                           || (a_statement->type
83                                               == AT_PAGE_RULE_STMT)), NULL);
84
85         result = g_try_malloc (sizeof (CRDeclaration));
86         if (!result) {
87                 cr_utils_trace_info ("Out of memory");
88                 return NULL;
89         }
90         memset (result, 0, sizeof (CRDeclaration));
91         result->property = a_property;
92         result->value = a_value;
93
94         if (a_value) {
95                 cr_term_ref (a_value);
96         }
97         result->parent_statement = a_statement;
98         return result;
99 }
100
101 /**
102  * cr_declaration_parse_from_buf:
103  *@a_statement: the parent css2 statement of this
104  *this declaration. Must be non NULL and of type
105  *RULESET_STMT (must be a ruleset).
106  *@a_str: the string that contains the statement.
107  *@a_enc: the encoding of a_str.
108  *
109  *Parses a text buffer that contains
110  *a css declaration.
111  *Returns the parsed declaration, or NULL in case of error.
112  */
113 CRDeclaration *
114 cr_declaration_parse_from_buf (CRStatement * a_statement,
115                                const guchar * a_str, enum CREncoding a_enc)
116 {
117         enum CRStatus status = CR_OK;
118         CRTerm *value = NULL;
119         CRString *property = NULL;
120         CRDeclaration *result = NULL;
121         CRParser *parser = NULL;
122         gboolean important = FALSE;
123
124         g_return_val_if_fail (a_str, NULL);
125         if (a_statement)
126                 g_return_val_if_fail (a_statement->type == RULESET_STMT,
127                                       NULL);
128
129         parser = cr_parser_new_from_buf ((guchar*)a_str, strlen (a_str), a_enc, FALSE);
130         g_return_val_if_fail (parser, NULL);
131
132         status = cr_parser_try_to_skip_spaces_and_comments (parser);
133         if (status != CR_OK)
134                 goto cleanup;
135
136         status = cr_parser_parse_declaration (parser, &property,
137                                               &value, &important);
138         if (status != CR_OK || !property)
139                 goto cleanup;
140
141         result = cr_declaration_new (a_statement, property, value);
142         if (result) {
143                 property = NULL;
144                 value = NULL;
145                 result->important = important;
146         }
147
148       cleanup:
149
150         if (parser) {
151                 cr_parser_destroy (parser);
152                 parser = NULL;
153         }
154
155         if (property) {
156                 cr_string_destroy (property);
157                 property = NULL;
158         }
159
160         if (value) {
161                 cr_term_destroy (value);
162                 value = NULL;
163         }
164
165         return result;
166 }
167
168 /**
169  * cr_declaration_parse_list_from_buf:
170  *@a_str: the input buffer that contains the list of declaration to
171  *parse.
172  *@a_enc: the encoding of a_str
173  *
174  *Parses a ';' separated list of properties declaration.
175  *Returns the parsed list of declaration, NULL if parsing failed.
176  */
177 CRDeclaration *
178 cr_declaration_parse_list_from_buf (const guchar * a_str,
179                                     enum CREncoding a_enc)
180 {
181
182         enum CRStatus status = CR_OK;
183         CRTerm *value = NULL;
184         CRString *property = NULL;
185         CRDeclaration *result = NULL,
186                 *cur_decl = NULL;
187         CRParser *parser = NULL;
188         CRTknzr *tokenizer = NULL;
189         gboolean important = FALSE;
190
191         g_return_val_if_fail (a_str, NULL);
192
193         parser = cr_parser_new_from_buf ((guchar*)a_str, strlen (a_str), a_enc, FALSE);
194         g_return_val_if_fail (parser, NULL);
195         status = cr_parser_get_tknzr (parser, &tokenizer);
196         if (status != CR_OK || !tokenizer) {
197                 if (status == CR_OK)
198                         status = CR_ERROR;
199                 goto cleanup;
200         }
201         status = cr_parser_try_to_skip_spaces_and_comments (parser);
202         if (status != CR_OK)
203                 goto cleanup;
204
205         status = cr_parser_parse_declaration (parser, &property,
206                                               &value, &important);
207         if (status != CR_OK || !property) {
208                 if (status != CR_OK)
209                         status = CR_ERROR;
210                 goto cleanup;
211         }
212         result = cr_declaration_new (NULL, property, value);
213         if (result) {
214                 property = NULL;
215                 value = NULL;
216                 result->important = important;
217         }
218         /*now, go parse the other declarations */
219         for (;;) {
220                 guint32 c = 0;
221
222                 cr_parser_try_to_skip_spaces_and_comments (parser);
223                 status = cr_tknzr_peek_char (tokenizer, &c);
224                 if (status != CR_OK) {
225                         if (status == CR_END_OF_INPUT_ERROR)
226                                 status = CR_OK;
227                         goto cleanup;
228                 }
229                 if (c == ';') {
230                         status = cr_tknzr_read_char (tokenizer, &c);
231                 } else {
232                         break;
233                 }
234                 important = FALSE;
235                 cr_parser_try_to_skip_spaces_and_comments (parser);
236                 status = cr_parser_parse_declaration (parser, &property,
237                                                       &value, &important);
238                 if (status != CR_OK || !property) {
239                         if (status == CR_END_OF_INPUT_ERROR) {
240                                 status = CR_OK;
241                         }
242                         break;
243                 }
244                 cur_decl = cr_declaration_new (NULL, property, value);
245                 if (cur_decl) {
246                         cur_decl->important = important;
247                         result = cr_declaration_append (result, cur_decl);
248                         property = NULL;
249                         value = NULL;
250                         cur_decl = NULL;
251                 } else {
252                         break;
253                 }
254         }
255
256       cleanup:
257
258         if (parser) {
259                 cr_parser_destroy (parser);
260                 parser = NULL;
261         }
262
263         if (property) {
264                 cr_string_destroy (property);
265                 property = NULL;
266         }
267
268         if (value) {
269                 cr_term_destroy (value);
270                 value = NULL;
271         }
272
273         if (status != CR_OK && result) {
274                 cr_declaration_destroy (result);
275                 result = NULL;
276         }
277         return result;
278 }
279
280 /**
281  * cr_declaration_append:
282  *@a_this: the current declaration list.
283  *@a_new: the declaration to append.
284  *
285  *Appends a new declaration to the current declarations list.
286  *Returns the declaration list with a_new appended to it, or NULL
287  *in case of error.
288  */
289 CRDeclaration *
290 cr_declaration_append (CRDeclaration * a_this, CRDeclaration * a_new)
291 {
292         CRDeclaration *cur = NULL;
293
294         g_return_val_if_fail (a_new, NULL);
295
296         if (!a_this)
297                 return a_new;
298
299         for (cur = a_this; cur && cur->next; cur = cur->next) ;
300
301         cur->next = a_new;
302         a_new->prev = cur;
303
304         return a_this;
305 }
306
307 /**
308  * cr_declaration_unlink:
309  *@a_decls: the declaration to unlink.
310  *
311  *Unlinks the declaration from the declaration list.
312  *case of a successfull completion, NULL otherwise.
313  *
314  *Returns a pointer to the unlinked declaration in
315  */
316 CRDeclaration *
317 cr_declaration_unlink (CRDeclaration * a_decl)
318 {
319         CRDeclaration *result = a_decl;
320
321         g_return_val_if_fail (result, NULL);
322
323         /*
324          *some sanity checks first
325          */
326         if (a_decl->prev) {
327                 g_return_val_if_fail (a_decl->prev->next == a_decl, NULL);
328
329         }
330         if (a_decl->next) {
331                 g_return_val_if_fail (a_decl->next->prev == a_decl, NULL);
332         }
333
334         /*
335          *now, the real unlinking job.
336          */
337         if (a_decl->prev) {
338                 a_decl->prev->next = a_decl->next;
339         }
340         if (a_decl->next) {
341                 a_decl->next->prev = a_decl->prev;
342         }
343         if (a_decl->parent_statement) {
344                 CRDeclaration **children_decl_ptr = NULL;
345
346                 switch (a_decl->parent_statement->type) {
347                 case RULESET_STMT:
348                         if (a_decl->parent_statement->kind.ruleset) {
349                                 children_decl_ptr =
350                                         &a_decl->parent_statement->
351                                         kind.ruleset->decl_list;
352                         }
353
354                         break;
355
356                 case AT_FONT_FACE_RULE_STMT:
357                         if (a_decl->parent_statement->kind.font_face_rule) {
358                                 children_decl_ptr =
359                                         &a_decl->parent_statement->
360                                         kind.font_face_rule->decl_list;
361                         }
362                         break;
363                 case AT_PAGE_RULE_STMT:
364                         if (a_decl->parent_statement->kind.page_rule) {
365                                 children_decl_ptr =
366                                         &a_decl->parent_statement->
367                                         kind.page_rule->decl_list;
368                         }
369
370                 default:
371                         break;
372                 }
373                 if (children_decl_ptr
374                     && *children_decl_ptr && *children_decl_ptr == a_decl)
375                         *children_decl_ptr = (*children_decl_ptr)->next;
376         }
377
378         a_decl->next = NULL;
379         a_decl->prev = NULL;
380         a_decl->parent_statement = NULL;
381
382         return result;
383 }
384
385 /**
386  * cr_declaration_prepend:
387  * @a_this: the current declaration list.
388  * @a_new: the declaration to prepend.
389  *
390  * prepends a declaration to the current declaration list.
391  *
392  * Returns the list with a_new prepended or NULL in case of error.
393  */
394 CRDeclaration *
395 cr_declaration_prepend (CRDeclaration * a_this, CRDeclaration * a_new)
396 {
397         CRDeclaration *cur = NULL;
398
399         g_return_val_if_fail (a_new, NULL);
400
401         if (!a_this)
402                 return a_new;
403
404         a_this->prev = a_new;
405         a_new->next = a_this;
406
407         for (cur = a_new; cur && cur->prev; cur = cur->prev) ;
408
409         return cur;
410 }
411
412 /**
413  * cr_declaration_append2:
414  *@a_this: the current declaration list.
415  *@a_prop: the property string of the declaration to append.
416  *@a_value: the value of the declaration to append.
417  *
418  *Appends a declaration to the current declaration list.
419  *Returns the list with the new property appended to it, or NULL in
420  *case of an error.
421  */
422 CRDeclaration *
423 cr_declaration_append2 (CRDeclaration * a_this,
424                         CRString * a_prop, CRTerm * a_value)
425 {
426         CRDeclaration *new_elem = NULL;
427
428         if (a_this) {
429                 new_elem = cr_declaration_new (a_this->parent_statement,
430                                                a_prop, a_value);
431         } else {
432                 new_elem = cr_declaration_new (NULL, a_prop, a_value);
433         }
434
435         g_return_val_if_fail (new_elem, NULL);
436
437         return cr_declaration_append (a_this, new_elem);
438 }
439
440 /**
441  * cr_declaration_dump:
442  *@a_this: the current instance of #CRDeclaration.
443  *@a_fp: the destination file.
444  *@a_indent: the number of indentation white char.
445  *@a_one_per_line: whether to put one declaration per line of not .
446  *
447  *
448  *Dumps a declaration list to a file.
449  */
450 void
451 cr_declaration_dump (CRDeclaration * a_this, FILE * a_fp, glong a_indent,
452                      gboolean a_one_per_line)
453 {
454         CRDeclaration *cur = NULL;
455
456         g_return_if_fail (a_this);
457
458         for (cur = a_this; cur; cur = cur->next) {
459                 if (cur->prev) {
460                         if (a_one_per_line == TRUE)
461                                 fprintf (a_fp, ";\n");
462                         else
463                                 fprintf (a_fp, "; ");
464                 }
465                 dump (cur, a_fp, a_indent);
466         }
467 }
468
469 /**
470  * cr_declaration_dump_one:
471  *@a_this: the current instance of #CRDeclaration.
472  *@a_fp: the destination file.
473  *@a_indent: the number of indentation white char.
474  *
475  *Dumps the first declaration of the declaration list to a file.
476  */
477 void
478 cr_declaration_dump_one (CRDeclaration * a_this, FILE * a_fp, glong a_indent)
479 {
480         g_return_if_fail (a_this);
481
482         dump (a_this, a_fp, a_indent);
483 }
484
485 /**
486  * cr_declaration_to_string:
487  *@a_this: the current instance of #CRDeclaration.
488  *@a_indent: the number of indentation white char
489  *to put before the actual serialisation.
490  *
491  *Serializes the declaration into a string
492  *Returns the serialized form the declaration. The caller must
493  *free the string using g_free().
494  */
495 gchar *
496 cr_declaration_to_string (CRDeclaration * a_this, gulong a_indent)
497 {
498         GString *stringue = NULL;
499
500         guchar *str = NULL,
501                 *result = NULL;
502
503         g_return_val_if_fail (a_this, NULL);
504
505         stringue = g_string_new (NULL);
506
507         if (a_this->property 
508             && a_this->property->stryng
509             && a_this->property->stryng->str) {
510                 str = g_strndup (a_this->property->stryng->str,
511                                  a_this->property->stryng->len);
512                 if (str) {
513                         cr_utils_dump_n_chars2 (' ', stringue, 
514                                                 a_indent);
515                         g_string_append (stringue, str);
516                         g_free (str);
517                         str = NULL;
518                 } else
519                         goto error;
520
521                 if (a_this->value) {
522                         guchar *value_str = NULL;
523
524                         value_str = cr_term_to_string (a_this->value);
525                         if (value_str) {
526                                 g_string_append_printf (stringue, " : %s",
527                                                         value_str);
528                                 g_free (value_str);
529                         } else
530                                 goto error;
531                 }
532                 if (a_this->important == TRUE) {
533                         g_string_append_printf (stringue, " %s",
534                                                 "!important");
535                 }
536         }
537         if (stringue && stringue->str) {
538                 result = stringue->str;
539                 g_string_free (stringue, FALSE);
540         }
541         return result;
542
543       error:
544         if (stringue) {
545                 g_string_free (stringue, TRUE);
546                 stringue = NULL;
547         }
548         if (str) {
549                 g_free (str);
550                 str = NULL;
551         }
552
553         return result;
554 }
555
556 /**
557  * cr_declaration_list_to_string:
558  *@a_this: the current instance of #CRDeclaration.
559  *@a_indent: the number of indentation white char
560  *to put before the actual serialisation.
561  *
562  *Serializes the declaration list into a string
563  */
564 guchar *
565 cr_declaration_list_to_string (CRDeclaration * a_this, gulong a_indent)
566 {
567         CRDeclaration *cur = NULL;
568         GString *stringue = NULL;
569         guchar *str = NULL,
570                 *result = NULL;
571
572         g_return_val_if_fail (a_this, NULL);
573
574         stringue = g_string_new (NULL);
575
576         for (cur = a_this; cur; cur = cur->next) {
577                 str = cr_declaration_to_string (cur, a_indent);
578                 if (str) {
579                         g_string_append_printf (stringue, "%s;", str);
580                         g_free (str);
581                 } else
582                         break;
583         }
584         if (stringue && stringue->str) {
585                 result = stringue->str;
586                 g_string_free (stringue, FALSE);
587         }
588
589         return result;
590 }
591
592 /**
593  * cr_declaration_list_to_string2:
594  *@a_this: the current instance of #CRDeclaration.
595  *@a_indent: the number of indentation white char
596  @a_one_decl_per_line: whether to output one doc per line or not.
597  *to put before the actual serialisation.
598  *
599  *Serializes the declaration list into a string
600  *Returns the serialized form the declararation.
601  */
602 guchar *
603 cr_declaration_list_to_string2 (CRDeclaration * a_this,
604                                 gulong a_indent, gboolean a_one_decl_per_line)
605 {
606         CRDeclaration *cur = NULL;
607         GString *stringue = NULL;
608         guchar *str = NULL,
609                 *result = NULL;
610
611         g_return_val_if_fail (a_this, NULL);
612
613         stringue = g_string_new (NULL);
614
615         for (cur = a_this; cur; cur = cur->next) {
616                 str = cr_declaration_to_string (cur, a_indent);
617                 if (str) {
618                         if (a_one_decl_per_line == TRUE) {
619                                 if (cur->next)
620                                         g_string_append_printf (stringue,
621                                                                 "%s;\n", str);
622                                 else
623                                         g_string_append (stringue,
624                                                          str);
625                         } else {
626                                 if (cur->next)
627                                         g_string_append_printf (stringue,
628                                                                 "%s;", str);
629                                 else
630                                         g_string_append (stringue,
631                                                          str);
632                         }
633                         g_free (str);
634                 } else
635                         break;
636         }
637         if (stringue && stringue->str) {
638                 result = stringue->str;
639                 g_string_free (stringue, FALSE);
640         }
641
642         return result;
643 }
644
645 /**
646  * cr_declaration_nr_props:
647  *@a_this: the current instance of #CRDeclaration.
648  *Return the number of properties in the declaration
649  */
650 gint
651 cr_declaration_nr_props (CRDeclaration * a_this)
652 {
653         CRDeclaration *cur = NULL;
654         int nr = 0;
655
656         g_return_val_if_fail (a_this, -1);
657
658         for (cur = a_this; cur; cur = cur->next)
659                 nr++;
660         return nr;
661 }
662
663 /**
664  * cr_declaration_get_from_list:
665  *@a_this: the current instance of #CRDeclaration.
666  *@itemnr: the index into the declaration list.
667  *
668  *Use an index to get a CRDeclaration from the declaration list.
669  *
670  *Returns #CRDeclaration at position itemnr, 
671  *if itemnr > number of declarations - 1,
672  *it will return NULL.
673  */
674 CRDeclaration *
675 cr_declaration_get_from_list (CRDeclaration * a_this, int itemnr)
676 {
677         CRDeclaration *cur = NULL;
678         int nr = 0;
679
680         g_return_val_if_fail (a_this, NULL);
681
682         for (cur = a_this; cur; cur = cur->next)
683                 if (nr++ == itemnr)
684                         return cur;
685         return NULL;
686 }
687
688 /**
689  * cr_declaration_get_by_prop_name:
690  *@a_this: the current instance of #CRDeclaration.
691  *@a_prop: the property name to search for.
692  *
693  *Use property name to get a CRDeclaration from the declaration list.
694  *Returns #CRDeclaration with property name a_prop, or NULL if not found.
695  */
696 CRDeclaration *
697 cr_declaration_get_by_prop_name (CRDeclaration * a_this,
698                                  const guchar * a_prop)
699 {
700         CRDeclaration *cur = NULL;
701
702         g_return_val_if_fail (a_this, NULL);
703         g_return_val_if_fail (a_prop, NULL);
704
705         for (cur = a_this; cur; cur = cur->next) {
706                 if (cur->property 
707                     && cur->property->stryng
708                     && cur->property->stryng->str) {
709                         if (!strcmp (cur->property->stryng->str, 
710                                      a_prop)) {
711                                 return cur;
712                         }
713                 }
714         }
715         return NULL;
716 }
717
718 /**
719  * cr_declaration_ref:
720  *@a_this: the current instance of #CRDeclaration.
721  *
722  *Increases the ref count of the current instance of #CRDeclaration.
723  */
724 void
725 cr_declaration_ref (CRDeclaration * a_this)
726 {
727         g_return_if_fail (a_this);
728
729         a_this->ref_count++;
730 }
731
732 /**
733  * cr_declaration_unref:
734  *@param a_this the current instance of #CRDeclaration.
735  *@return TRUE if the current instance of #CRDeclaration has been destroyed
736  *(ref count reached zero), FALSE otherwise.
737  *
738  *Decrements the ref count of the current instance of #CRDeclaration.
739  *If the ref count reaches zero, the current instance of #CRDeclaration
740  *if destroyed.
741  *Returns TRUE if the object got destroyed, FALSE otherwise.
742  */
743 gboolean
744 cr_declaration_unref (CRDeclaration * a_this)
745 {
746         g_return_val_if_fail (a_this, FALSE);
747
748         if (a_this->ref_count) {
749                 a_this->ref_count--;
750         }
751
752         if (a_this->ref_count == 0) {
753                 cr_declaration_destroy (a_this);
754                 return TRUE;
755         }
756         return FALSE;
757 }
758
759 /**
760  * cr_declaration_destroy:
761  *@a_this: the current instance of #CRDeclaration.
762  *
763  *Destructor of the declaration list.
764  */
765 void
766 cr_declaration_destroy (CRDeclaration * a_this)
767 {
768         CRDeclaration *cur = NULL;
769
770         g_return_if_fail (a_this);
771
772         /*
773          *Go get the tail of the list.
774          *Meanwhile, free each property/value pair contained in the list.
775          */
776         for (cur = a_this; cur && cur->next; cur = cur->next) {
777                 if (cur->property) {
778                         cr_string_destroy (cur->property);
779                         cur->property = NULL;
780                 }
781
782                 if (cur->value) {
783                         cr_term_destroy (cur->value);
784                         cur->value = NULL;
785                 }
786         }
787
788         if (cur) {
789                 if (cur->property) {
790                         cr_string_destroy (cur->property);
791                         cur->property = NULL;
792                 }
793
794                 if (cur->value) {
795                         cr_term_destroy (cur->value);
796                         cur->value = NULL;
797                 }
798         }
799
800         /*in case the list contains only one element */
801         if (cur && !cur->prev) {
802                 g_free (cur);
803                 return;
804         }
805
806         /*walk backward the list and free each "next" element */
807         for (cur = cur->prev; cur && cur->prev; cur = cur->prev) {
808                 if (cur->next) {
809                         g_free (cur->next);
810                         cur->next = NULL;
811                 }
812         }
813
814         if (!cur)
815                 return;
816
817         if (cur->next) {
818                 g_free (cur->next);
819                 cur->next = NULL;
820         }
821
822         g_free (cur);
823 }