00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include <string.h>
00025 #include "cr-sel-eng.h"
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035 #define PRIVATE(a_this) (a_this)->priv
00036
00037 struct CRPseudoClassSelHandlerEntry {
00038 guchar *name;
00039 enum CRPseudoType type;
00040 CRPseudoClassSelectorHandler handler;
00041 };
00042
00043 struct _CRSelEngPriv {
00044
00045 gboolean case_sensitive;
00046
00047 CRStyleSheet *sheet;
00048
00049
00050
00051
00052
00053 CRStatement *cur_stmt;
00054 GList *pcs_handlers;
00055 gint pcs_handlers_size;
00056 };
00057
00058 static gboolean class_add_sel_matches_node (CRAdditionalSel * a_add_sel,
00059 xmlNode * a_node);
00060
00061 static gboolean id_add_sel_matches_node (CRAdditionalSel * a_add_sel,
00062 xmlNode * a_node);
00063
00064 static gboolean attr_add_sel_matches_node (CRAdditionalSel * a_add_sel,
00065 xmlNode * a_node);
00066
00067 static enum CRStatus sel_matches_node_real (CRSelEng * a_this,
00068 CRSimpleSel * a_sel,
00069 xmlNode * a_node,
00070 gboolean * a_result,
00071 gboolean a_recurse);
00072
00073 static enum CRStatus cr_sel_eng_get_matched_rulesets_real (CRSelEng * a_this,
00074 CRStyleSheet *
00075 a_stylesheet,
00076 xmlNode * a_node,
00077 CRStatement **
00078 a_rulesets,
00079 gulong * a_len);
00080
00081 #ifndef NEW_PROPERTIES_GETTER
00082 static enum CRStatus put_css_properties_in_hashtable (GHashTable **
00083 a_props_hashtable,
00084 CRStatement *
00085 a_ruleset);
00086 static void set_style_from_props_hash_hr_func (gpointer a_prop,
00087 gpointer a_decl,
00088 gpointer a_style);
00089 #else
00090 static enum CRStatus put_css_properties_in_props_list (CRPropList ** a_props,
00091 CRStatement *
00092 a_ruleset);
00093 #endif
00094
00095 static gboolean pseudo_class_add_sel_matches_node (CRSelEng * a_this,
00096 CRAdditionalSel *
00097 a_add_sel,
00098 xmlNode * a_node);
00099
00100 static gboolean lang_pseudo_class_handler (CRSelEng * a_this,
00101 CRAdditionalSel * a_sel,
00102 xmlNode * a_node);
00103
00104 static gboolean first_child_pseudo_class_handler (CRSelEng * a_this,
00105 CRAdditionalSel * a_sel,
00106 xmlNode * a_node);
00107
00108 static xmlNode *get_next_element_node (xmlNode * a_node);
00109
00110 static xmlNode *get_next_child_element_node (xmlNode * a_node);
00111
00112 static xmlNode *get_prev_element_node (xmlNode * a_node);
00113
00114 static xmlNode *get_next_parent_element_node (xmlNode * a_node);
00115
00116 static gboolean
00117 lang_pseudo_class_handler (CRSelEng * a_this,
00118 CRAdditionalSel * a_sel, xmlNode * a_node)
00119 {
00120 xmlNode *node = a_node;
00121 xmlChar *val = NULL;
00122 gboolean result = FALSE;
00123
00124 g_return_val_if_fail (a_this && PRIVATE (a_this)
00125 && a_sel && a_sel->content.pseudo
00126 && a_sel->content.pseudo
00127 && a_sel->content.pseudo->name
00128 && a_node, CR_BAD_PARAM_ERROR);
00129
00130 if (strncmp (a_sel->content.pseudo->name->str, "lang", 4)
00131 || !a_sel->content.pseudo->type == FUNCTION_PSEUDO) {
00132 cr_utils_trace_info ("This handler is for :lang only");
00133 return CR_BAD_PSEUDO_CLASS_SEL_HANDLER_ERROR;
00134 }
00135
00136 if (!a_sel->content.pseudo->extra
00137 || a_sel->content.pseudo->extra->len < 2)
00138 return FALSE;
00139 for (; node; node = get_next_parent_element_node (node)) {
00140 val = xmlGetProp (node, "lang");
00141 if (val
00142 && !strncmp (val,
00143 a_sel->content.pseudo->extra->str,
00144 a_sel->content.pseudo->extra->len)) {
00145 result = TRUE;
00146 }
00147 if (val) {
00148 xmlFree (val);
00149 val = NULL;
00150 }
00151 }
00152
00153 return result;
00154 }
00155
00156 static gboolean
00157 first_child_pseudo_class_handler (CRSelEng * a_this,
00158 CRAdditionalSel * a_sel, xmlNode * a_node)
00159 {
00160 xmlNode *node = NULL;
00161
00162 g_return_val_if_fail (a_this && PRIVATE (a_this)
00163 && a_sel && a_sel->content.pseudo
00164 && a_sel->content.pseudo
00165 && a_sel->content.pseudo->name
00166 && a_node, CR_BAD_PARAM_ERROR);
00167
00168 if (strcmp (a_sel->content.pseudo->name->str, "first-child")
00169 || !a_sel->content.pseudo->type == IDENT_PSEUDO) {
00170 cr_utils_trace_info ("This handler is for :first-child only");
00171 return CR_BAD_PSEUDO_CLASS_SEL_HANDLER_ERROR;
00172 }
00173 if (!a_node->parent)
00174 return FALSE;
00175 node = get_next_child_element_node (a_node->parent);
00176 if (node == a_node)
00177 return TRUE;
00178 return FALSE;
00179 }
00180
00181 static gboolean
00182 pseudo_class_add_sel_matches_node (CRSelEng * a_this,
00183 CRAdditionalSel * a_add_sel,
00184 xmlNode * a_node)
00185 {
00186 enum CRStatus status = CR_OK;
00187 CRPseudoClassSelectorHandler handler = NULL;
00188
00189 g_return_val_if_fail (a_this && PRIVATE (a_this)
00190 && a_add_sel
00191 && a_add_sel->content.pseudo
00192 && a_add_sel->content.pseudo->name
00193 && a_add_sel->content.pseudo->name->str
00194 && a_node, CR_BAD_PARAM_ERROR);
00195
00196 status = cr_sel_eng_get_pseudo_class_selector_handler
00197 (a_this, a_add_sel->content.pseudo->name->str,
00198 a_add_sel->content.pseudo->type, &handler);
00199 if (status != CR_OK || !handler)
00200 return FALSE;
00201
00202 return handler (a_this, a_add_sel, a_node);
00203 }
00204
00205
00206
00207
00208
00209
00210
00211 static gboolean
00212 class_add_sel_matches_node (CRAdditionalSel * a_add_sel, xmlNode * a_node)
00213 {
00214 gboolean result = FALSE;
00215 xmlChar *klass = NULL,
00216 *cur = NULL;
00217
00218 g_return_val_if_fail (a_add_sel
00219 && a_add_sel->type == CLASS_ADD_SELECTOR
00220 && a_add_sel->content.class_name
00221 && a_add_sel->content.class_name->str
00222 && a_node, FALSE);
00223
00224 if (xmlHasProp (a_node, "class")) {
00225 klass = xmlGetProp (a_node, "class");
00226 for (cur = klass; cur && *cur; cur++) {
00227 while (cur && *cur
00228 && cr_utils_is_white_space (*cur)
00229 == TRUE)
00230 cur++;
00231 if (!*cur)
00232 break;
00233 if (!strncmp (cur, a_add_sel->content.class_name->str,
00234 a_add_sel->content.class_name->len)) {
00235 cur += a_add_sel->content.class_name->len;
00236 if ((cur && !*cur)
00237 || cr_utils_is_white_space (*cur) == TRUE)
00238 result = TRUE;
00239 }
00240 if (cur && !*cur)
00241 break ;
00242 }
00243 }
00244
00245 if (klass) {
00246 xmlFree (klass);
00247 klass = NULL;
00248 }
00249 return result;
00250
00251 }
00252
00253
00254
00255
00256
00257
00258
00259 static gboolean
00260 id_add_sel_matches_node (CRAdditionalSel * a_add_sel, xmlNode * a_node)
00261 {
00262 gboolean result = FALSE;
00263 xmlChar *id = NULL;
00264
00265 g_return_val_if_fail (a_add_sel
00266 && a_add_sel->type == ID_ADD_SELECTOR
00267 && a_add_sel->content.id_name
00268 && a_add_sel->content.id_name->str
00269 && a_node, FALSE);
00270 g_return_val_if_fail (a_add_sel
00271 && a_add_sel->type == ID_ADD_SELECTOR
00272 && a_node, FALSE);
00273
00274 if (xmlHasProp (a_node, "id")) {
00275 id = xmlGetProp (a_node, "id");
00276 if (!strncmp (id, a_add_sel->content.id_name->str,
00277 a_add_sel->content.id_name->len)) {
00278 result = TRUE;
00279 }
00280 }
00281
00282 if (id) {
00283 xmlFree (id);
00284 id = NULL;
00285 }
00286 return result;
00287 }
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298 static gboolean
00299 attr_add_sel_matches_node (CRAdditionalSel * a_add_sel, xmlNode * a_node)
00300 {
00301 CRAttrSel *cur_sel = NULL;
00302
00303 g_return_val_if_fail (a_add_sel
00304 && a_add_sel->type == ATTRIBUTE_ADD_SELECTOR
00305 && a_node, FALSE);
00306
00307 for (cur_sel = a_add_sel->content.attr_sel;
00308 cur_sel; cur_sel = cur_sel->next) {
00309 switch (cur_sel->match_way) {
00310 case SET:
00311 if (!cur_sel->name || !cur_sel->name->str)
00312 return FALSE;
00313
00314 if (!xmlHasProp (a_node, cur_sel->name->str))
00315 return FALSE;
00316 break;
00317
00318 case EQUALS:
00319 {
00320 xmlChar *value = NULL;
00321
00322 if (!cur_sel->name || !cur_sel->name->str
00323 || !cur_sel->value
00324 || !cur_sel->value->str)
00325 return FALSE;
00326
00327 if (!xmlHasProp (a_node, cur_sel->name->str))
00328 return FALSE;
00329
00330 value = xmlGetProp (a_node,
00331 cur_sel->name->str);
00332
00333 if (value
00334 && strncmp (value, cur_sel->value->str,
00335 cur_sel->value->len)) {
00336 xmlFree (value);
00337 return FALSE;
00338 }
00339 xmlFree (value);
00340 }
00341 break;
00342
00343 case INCLUDES:
00344 {
00345 xmlChar *value = NULL,
00346 *ptr1 = NULL,
00347 *ptr2 = NULL,
00348 *cur = NULL;
00349 gboolean found = FALSE;
00350
00351 if (!xmlHasProp (a_node, cur_sel->name->str))
00352 return FALSE;
00353 value = xmlGetProp (a_node,
00354 cur_sel->name->str);
00355
00356 if (!value)
00357 return FALSE;
00358
00359
00360
00361
00362
00363
00364 for (cur = value; *cur; cur++) {
00365
00366
00367
00368
00369 while (cr_utils_is_white_space
00370 (*cur) == TRUE && *cur)
00371 cur++;
00372 if (!*cur)
00373 break;
00374 ptr1 = cur;
00375
00376
00377
00378
00379 while (cr_utils_is_white_space
00380 (*cur) == FALSE && *cur)
00381 cur++;
00382 if (!*cur)
00383 break;
00384 cur--;
00385 ptr2 = cur;
00386
00387 if (!strncmp
00388 (ptr1, cur_sel->value->str,
00389 ptr2 - ptr1 + 1)) {
00390 found = TRUE;
00391 break;
00392 }
00393 ptr1 = ptr2 = NULL;
00394 }
00395
00396 if (found == FALSE) {
00397 xmlFree (value);
00398 return FALSE;
00399 }
00400 xmlFree (value);
00401 }
00402 break;
00403
00404 case DASHMATCH:
00405 {
00406 xmlChar *value = NULL,
00407 *ptr1 = NULL,
00408 *ptr2 = NULL,
00409 *cur = NULL;
00410 gboolean found = FALSE;
00411
00412 if (!xmlHasProp (a_node, cur_sel->name->str))
00413 return FALSE;
00414 value = xmlGetProp (a_node,
00415 cur_sel->name->str);
00416
00417
00418
00419
00420
00421
00422 for (cur = value; *cur; cur++) {
00423 if (*cur == '-')
00424 cur++;
00425 ptr1 = cur;
00426
00427 while (*cur != '-' && *cur)
00428 cur++;
00429 if (!*cur)
00430 break;
00431 cur--;
00432 ptr2 = cur;
00433
00434 if (g_strstr_len
00435 (ptr1, ptr2 - ptr1 + 1,
00436 cur_sel->value->str)
00437 == (gchar *) ptr1) {
00438 found = TRUE;
00439 break;
00440 }
00441 }
00442
00443 if (found == FALSE) {
00444 xmlFree (value);
00445 return FALSE;
00446 }
00447 xmlFree (value);
00448 }
00449 break;
00450 default:
00451 return FALSE;
00452 }
00453 }
00454
00455 return TRUE;
00456 }
00457
00458
00459
00460
00461
00462
00463
00464 static gboolean
00465 additional_selector_matches_node (CRSelEng * a_this,
00466 CRAdditionalSel * a_add_sel,
00467 xmlNode * a_node)
00468 {
00469 if (!a_add_sel) {
00470 return FALSE;
00471 }
00472
00473 if (a_add_sel->type == NO_ADD_SELECTOR) {
00474 return FALSE;
00475 }
00476
00477 if (a_add_sel->type == CLASS_ADD_SELECTOR
00478 && a_add_sel->content.class_name
00479 && a_add_sel->content.class_name->str) {
00480 if (class_add_sel_matches_node (a_add_sel, a_node) == FALSE) {
00481 return FALSE;
00482 }
00483 return TRUE;
00484 } else if (a_add_sel->type == ID_ADD_SELECTOR
00485 && a_add_sel->content.id_name
00486 && a_add_sel->content.id_name->str) {
00487 if (id_add_sel_matches_node (a_add_sel, a_node) == FALSE) {
00488 return FALSE;
00489 }
00490 return TRUE;
00491 } else if (a_add_sel->type == ATTRIBUTE_ADD_SELECTOR
00492 && a_add_sel->content.attr_sel) {
00493
00494
00495
00496
00497
00498 if (attr_add_sel_matches_node (a_add_sel, a_node)
00499 == FALSE) {
00500 return FALSE;
00501 }
00502 return TRUE;
00503 } else if (a_add_sel->type == PSEUDO_CLASS_ADD_SELECTOR
00504 && a_add_sel->content.pseudo) {
00505 if (pseudo_class_add_sel_matches_node
00506 (a_this, a_add_sel, a_node) == TRUE) {
00507 return TRUE;
00508 }
00509 return FALSE;
00510 }
00511 return FALSE;
00512 }
00513
00514 static xmlNode *
00515 get_next_element_node (xmlNode * a_node)
00516 {
00517 xmlNode *cur_node = NULL;
00518
00519 g_return_val_if_fail (a_node, NULL);
00520
00521 cur_node = a_node->next;
00522 while (cur_node && cur_node->type != XML_ELEMENT_NODE) {
00523 cur_node = cur_node->next;
00524 }
00525 return cur_node;
00526 }
00527
00528 static xmlNode *
00529 get_next_child_element_node (xmlNode * a_node)
00530 {
00531 xmlNode *cur_node = NULL;
00532
00533 g_return_val_if_fail (a_node, NULL);
00534
00535 cur_node = a_node->children;
00536 if (!cur_node)
00537 return cur_node;
00538 if (a_node->children->type == XML_ELEMENT_NODE)
00539 return a_node->children;
00540 return get_next_element_node (a_node->children);
00541 }
00542
00543 static xmlNode *
00544 get_prev_element_node (xmlNode * a_node)
00545 {
00546 xmlNode *cur_node = NULL;
00547
00548 g_return_val_if_fail (a_node, NULL);
00549
00550 cur_node = a_node->prev;
00551 while (cur_node && cur_node->type != XML_ELEMENT_NODE) {
00552 cur_node = cur_node->prev;
00553 }
00554 return cur_node;
00555 }
00556
00557 static xmlNode *
00558 get_next_parent_element_node (xmlNode * a_node)
00559 {
00560 xmlNode *cur_node = NULL;
00561
00562 g_return_val_if_fail (a_node, NULL);
00563
00564 cur_node = a_node->parent;
00565 while (cur_node && cur_node->type != XML_ELEMENT_NODE) {
00566 cur_node = cur_node->parent;
00567 }
00568 return cur_node;
00569 }
00570
00571
00572
00573
00574
00575
00576
00577
00578
00579
00580
00581
00582
00583
00584
00585
00586
00587
00588
00589 static enum CRStatus
00590 sel_matches_node_real (CRSelEng * a_this, CRSimpleSel * a_sel,
00591 xmlNode * a_node, gboolean * a_result,
00592 gboolean a_recurse)
00593 {
00594 CRSimpleSel *cur_sel = NULL;
00595 xmlNode *cur_node = NULL;
00596
00597 g_return_val_if_fail (a_this && PRIVATE (a_this)
00598 && a_this && a_node
00599 && a_result, CR_BAD_PARAM_ERROR);
00600
00601 *a_result = FALSE;
00602
00603 if (a_node->type != XML_ELEMENT_NODE)
00604 return CR_OK;
00605
00606 if (a_recurse == TRUE) {
00607
00608 for (cur_sel = a_sel;
00609 cur_sel && cur_sel->next; cur_sel = cur_sel->next) ;
00610 } else {
00611 cur_sel = a_sel;
00612 }
00613
00614 for (cur_node = a_node; cur_sel; cur_sel = cur_sel->prev) {
00615 if (cur_sel->type_mask & UNIVERSAL_SELECTOR) {
00616 goto walk_a_step_in_expr;
00617 } else if (cur_sel->type_mask & TYPE_SELECTOR) {
00618 if (cur_sel && cur_sel->name && cur_sel->name->str) {
00619 if (!strcmp (cur_sel->name->str,
00620 cur_node->name)) {
00621
00622
00623
00624
00625
00626
00627
00628 if (cur_sel->add_sel) {
00629 if (additional_selector_matches_node (a_this, cur_sel->add_sel, cur_node) == TRUE) {
00630 goto walk_a_step_in_expr;
00631 } else {
00632 goto done;
00633 }
00634 } else {
00635 goto walk_a_step_in_expr;
00636 }
00637 }
00638 goto done;
00639 } else {
00640 goto done;
00641 }
00642 }
00643
00644 if (!cur_sel->add_sel) {
00645 goto done;
00646 }
00647 if (additional_selector_matches_node
00648 (a_this, cur_sel->add_sel, cur_node) == TRUE) {
00649 goto walk_a_step_in_expr;
00650 } else {
00651 goto done;
00652 }
00653
00654 walk_a_step_in_expr:
00655 if (a_recurse == FALSE) {
00656 *a_result = TRUE;
00657 goto done;
00658 }
00659
00660
00661
00662
00663
00664
00665 if (!cur_sel->prev)
00666 break;
00667
00668 switch (cur_sel->combinator) {
00669 case NO_COMBINATOR:
00670 break;
00671
00672 case COMB_WS:
00673 {
00674 xmlNode *n = NULL;
00675 enum CRStatus status = CR_OK;
00676 gboolean matches = FALSE;
00677
00678
00679
00680
00681
00682 for (n = cur_node->parent; n; n = n->parent) {
00683 status = sel_matches_node_real
00684 (a_this, cur_sel->prev,
00685 n, &matches, FALSE);
00686 if (status != CR_OK)
00687 goto done;
00688 if (matches == TRUE) {
00689 cur_node = n;
00690 break;
00691 }
00692 }
00693
00694 if (!n) {
00695
00696
00697
00698
00699 goto done;
00700 }
00701
00702
00703
00704
00705
00706
00707
00708
00709 break;
00710 }
00711
00712 case COMB_PLUS:
00713 cur_node = get_prev_element_node (cur_node);
00714 if (!cur_node)
00715 goto done;
00716 break;
00717
00718 case COMB_GT:
00719 cur_node = get_next_parent_element_node (cur_node);
00720 if (!cur_node)
00721 goto done;
00722 break;
00723
00724 default:
00725 goto done;
00726 }
00727 continue;
00728 }
00729
00730
00731
00732
00733
00734 *a_result = TRUE;
00735
00736 done:
00737 return CR_OK;
00738 }
00739
00740
00741
00742
00743
00744
00745
00746
00747
00748
00749
00750
00751
00752
00753
00754
00755
00756
00757
00758
00759
00760
00761
00762
00763
00764
00765
00766
00767
00768
00769
00770
00771
00772
00773
00774
00775
00776
00777 static enum CRStatus
00778 cr_sel_eng_get_matched_rulesets_real (CRSelEng * a_this,
00779 CRStyleSheet * a_stylesheet,
00780 xmlNode * a_node,
00781 CRStatement ** a_rulesets,
00782 gulong * a_len)
00783 {
00784 CRStatement *cur_stmt = NULL;
00785 CRSelector *sel_list = NULL,
00786 *cur_sel = NULL;
00787 gboolean matches = FALSE;
00788 enum CRStatus status = CR_OK;
00789 gulong i = 0;
00790
00791 g_return_val_if_fail (a_this
00792 && a_stylesheet
00793 && a_node && a_rulesets, CR_BAD_PARAM_ERROR);
00794
00795 if (!a_stylesheet->statements) {
00796 *a_rulesets = NULL;
00797 *a_len = 0;
00798 return CR_OK;
00799 }
00800
00801
00802
00803
00804
00805 if (PRIVATE (a_this)->sheet != a_stylesheet) {
00806 PRIVATE (a_this)->sheet = a_stylesheet;
00807 PRIVATE (a_this)->cur_stmt = a_stylesheet->statements;
00808 }
00809
00810
00811
00812
00813
00814
00815
00816 for (cur_stmt = PRIVATE (a_this)->cur_stmt, i = 0;
00817 (PRIVATE (a_this)->cur_stmt = cur_stmt);
00818 cur_stmt = cur_stmt->next) {
00819
00820
00821
00822
00823 sel_list = NULL;
00824
00825
00826
00827
00828
00829 switch (cur_stmt->type) {
00830 case RULESET_STMT:
00831 if (cur_stmt->kind.ruleset
00832 && cur_stmt->kind.ruleset->sel_list) {
00833 sel_list = cur_stmt->kind.ruleset->sel_list;
00834 }
00835 break;
00836
00837 case AT_MEDIA_RULE_STMT:
00838 if (cur_stmt->kind.media_rule
00839 && cur_stmt->kind.media_rule->rulesets
00840 && cur_stmt->kind.media_rule->rulesets->
00841 kind.ruleset
00842 && cur_stmt->kind.media_rule->rulesets->
00843 kind.ruleset->sel_list) {
00844 sel_list =
00845 cur_stmt->kind.media_rule->
00846 rulesets->kind.ruleset->sel_list;
00847 }
00848 break;
00849
00850 case AT_IMPORT_RULE_STMT:
00851
00852
00853
00854
00855 break;
00856 default:
00857 break;
00858 }
00859
00860 if (!sel_list)
00861 continue;
00862
00863
00864
00865
00866
00867
00868 for (cur_sel = sel_list; cur_sel; cur_sel = cur_sel->next) {
00869 if (!cur_sel->simple_sel)
00870 continue;
00871
00872 status = cr_sel_eng_matches_node
00873 (a_this, cur_sel->simple_sel,
00874 a_node, &matches);
00875
00876 if (status == CR_OK && matches == TRUE) {
00877
00878
00879
00880
00881
00882
00883 if (i < *a_len) {
00884 a_rulesets[i] = cur_stmt;
00885 i++;
00886
00887
00888
00889
00890
00891
00892
00893
00894
00895
00896 status = cr_simple_sel_compute_specificity (cur_sel->simple_sel);
00897
00898 g_return_val_if_fail (status == CR_OK,
00899 CR_ERROR);
00900 cur_stmt->specificity =
00901 cur_sel->simple_sel->
00902 specificity;
00903 } else
00904 {
00905 *a_len = i;
00906 return CR_OUTPUT_TOO_SHORT_ERROR;
00907 }
00908 }
00909 }
00910 }
00911
00912
00913
00914
00915
00916
00917
00918 g_return_val_if_fail (!PRIVATE (a_this)->cur_stmt, CR_ERROR);
00919 PRIVATE (a_this)->sheet = NULL;
00920 *a_len = i;
00921 return CR_OK;
00922 }
00923
00924 #ifndef NEW_PROPERTIES_GETTER
00925
00926
00927
00928
00929
00930
00931
00932
00933
00934
00935
00936
00937
00938
00939
00940
00941
00942 static enum CRStatus
00943 put_css_properties_in_hashtable (GHashTable ** a_props_hashtable,
00944 CRStatement * a_stmt)
00945 {
00946 GHashTable *props_hash = NULL;
00947 CRDeclaration *cur_decl = NULL;
00948
00949 g_return_val_if_fail (a_props_hashtable && a_stmt
00950 && a_stmt->type == RULESET_STMT
00951 && a_stmt->kind.ruleset, CR_BAD_PARAM_ERROR);
00952
00953 if (!*a_props_hashtable) {
00954 *a_props_hashtable = g_hash_table_new (g_str_hash,
00955 g_str_equal);
00956 }
00957 props_hash = *a_props_hashtable;
00958
00959 for (cur_decl = a_stmt->kind.ruleset->decl_list;
00960 cur_decl; cur_decl = cur_decl->next) {
00961 CRDeclaration *decl = NULL;
00962
00963 if (!cur_decl->property || !cur_decl->property->str)
00964 continue;
00965
00966
00967
00968
00969
00970
00971
00972
00973 decl = g_hash_table_lookup
00974 (props_hash, cur_decl->property->str);
00975
00976 if (!decl) {
00977 g_hash_table_replace
00978 (props_hash,
00979 cur_decl->property->str, cur_decl);
00980 continue;
00981 }
00982
00983
00984
00985
00986
00987
00988
00989
00990
00991
00992
00993
00994
00995
00996
00997
00998 if (decl->parent_statement
00999 && decl->parent_statement->parent_sheet
01000 && (decl->parent_statement->parent_sheet->origin
01001 < a_stmt->parent_sheet->origin)) {
01002 g_hash_table_insert
01003 (props_hash,
01004 cur_decl->property->str, cur_decl);
01005 continue;
01006 } else if (decl->parent_statement
01007 && decl->parent_statement->parent_sheet
01008 && (decl->parent_statement->
01009 parent_sheet->origin
01010 > a_stmt->parent_sheet->origin)) {
01011
01012 continue;
01013 }
01014
01015
01016
01017
01018
01019
01020
01021
01022
01023
01024
01025
01026
01027 if (a_stmt->specificity
01028 >= decl->parent_statement->specificity) {
01029 g_hash_table_insert
01030 (props_hash,
01031 cur_decl->property->str, cur_decl);
01032 }
01033 }
01034 return CR_OK;
01035 }
01036
01037 static void
01038 set_style_from_props_hash_hr_func (gpointer a_prop, gpointer a_decl,
01039 gpointer a_style)
01040 {
01041 CRDeclaration *decl = a_decl;
01042 CRStyle *style = a_style;
01043
01044 g_return_if_fail (a_decl && a_prop && a_style);
01045
01046 cr_style_set_style_from_decl (style, decl);
01047 }
01048 #else
01049 static enum CRStatus
01050 put_css_properties_in_props_list (CRPropList ** a_props, CRStatement * a_stmt)
01051 {
01052 CRPropList *props = NULL,
01053 *pair = NULL,
01054 *tmp_props = NULL;
01055 CRDeclaration *cur_decl = NULL;
01056
01057 g_return_val_if_fail (a_props && a_stmt
01058 && a_stmt->type == RULESET_STMT
01059 && a_stmt->kind.ruleset, CR_BAD_PARAM_ERROR);
01060
01061 props = *a_props;
01062
01063 for (cur_decl = a_stmt->kind.ruleset->decl_list;
01064 cur_decl; cur_decl = cur_decl->next) {
01065 CRDeclaration *decl;
01066
01067 decl = NULL;
01068 pair = NULL;
01069
01070 if (!cur_decl->property || !cur_decl->property->str)
01071 continue;
01072
01073
01074
01075
01076
01077
01078
01079 cr_prop_list_lookup_prop (props, cur_decl->property, &pair);
01080
01081 if (!pair) {
01082 tmp_props = cr_prop_list_append2
01083 (props, cur_decl->property, cur_decl);
01084 if (tmp_props) {
01085 props = tmp_props;
01086 tmp_props = NULL;
01087 }
01088 continue;
01089 }
01090
01091
01092
01093
01094
01095
01096
01097 cr_prop_list_get_decl (pair, &decl);
01098 g_return_val_if_fail (decl, CR_ERROR);
01099
01100
01101
01102
01103
01104
01105
01106
01107
01108 if (decl->parent_statement
01109 && decl->parent_statement->parent_sheet
01110 && (decl->parent_statement->parent_sheet->origin
01111 < a_stmt->parent_sheet->origin)) {
01112
01113
01114
01115
01116
01117
01118
01119 if (decl->important == TRUE
01120 && decl->parent_statement->parent_sheet->origin
01121 != ORIGIN_UA) {
01122 continue;
01123 }
01124 tmp_props = cr_prop_list_unlink (props, pair);
01125 if (props) {
01126 cr_prop_list_destroy (pair);
01127 }
01128 props = tmp_props;
01129 tmp_props = NULL;
01130 props = cr_prop_list_append2
01131 (props, cur_decl->property, cur_decl);
01132
01133 continue;
01134 } else if (decl->parent_statement
01135 && decl->parent_statement->parent_sheet
01136 && (decl->parent_statement->
01137 parent_sheet->origin
01138 > a_stmt->parent_sheet->origin)) {
01139 cr_utils_trace_info
01140 ("We should not reach this line\n");
01141 continue;
01142 }
01143
01144
01145
01146
01147
01148
01149
01150
01151
01152
01153
01154
01155
01156 if (a_stmt->specificity
01157 >= decl->parent_statement->specificity) {
01158 if (decl->important == TRUE)
01159 continue;
01160 props = cr_prop_list_unlink (props, pair);
01161 if (pair) {
01162 cr_prop_list_destroy (pair);
01163 pair = NULL;
01164 }
01165 props = cr_prop_list_append2 (props,
01166 cur_decl->property,
01167 cur_decl);
01168 }
01169 }
01170
01171 *a_props = props;
01172
01173 return CR_OK;
01174 }
01175
01176 static void
01177 set_style_from_props (CRStyle * a_style, CRPropList * a_props)
01178 {
01179 CRPropList *cur = NULL;
01180 CRDeclaration *decl = NULL;
01181
01182 for (cur = a_props; cur; cur = cr_prop_list_get_next (cur)) {
01183 cr_prop_list_get_decl (cur, &decl);
01184 cr_style_set_style_from_decl (a_style, decl);
01185 decl = NULL;
01186 }
01187 }
01188 #endif
01189
01190
01191
01192
01193
01194
01195
01196
01197
01198
01199 CRSelEng *
01200 cr_sel_eng_new (void)
01201 {
01202 CRSelEng *result = NULL;
01203
01204 result = g_try_malloc (sizeof (CRSelEng));
01205 if (!result) {
01206 cr_utils_trace_info ("Out of memory");
01207 return NULL;
01208 }
01209 memset (result, 0, sizeof (CRSelEng));
01210
01211 PRIVATE (result) = g_try_malloc (sizeof (CRSelEngPriv));
01212 if (!PRIVATE (result)) {
01213 cr_utils_trace_info ("Out of memory");
01214 g_free (result);
01215 return NULL;
01216 }
01217 memset (PRIVATE (result), 0, sizeof (CRSelEngPriv));
01218 cr_sel_eng_register_pseudo_class_sel_handler
01219 (result, (guchar *) "first-child",
01220 IDENT_PSEUDO, (CRPseudoClassSelectorHandler)
01221 first_child_pseudo_class_handler);
01222 cr_sel_eng_register_pseudo_class_sel_handler
01223 (result, (guchar *) "lang",
01224 FUNCTION_PSEUDO, (CRPseudoClassSelectorHandler)
01225 lang_pseudo_class_handler);
01226
01227 return result;
01228 }
01229
01230
01231
01232
01233
01234
01235
01236
01237
01238
01239 enum CRStatus
01240 cr_sel_eng_register_pseudo_class_sel_handler (CRSelEng * a_this,
01241 guchar * a_name,
01242 enum CRPseudoType a_type,
01243 CRPseudoClassSelectorHandler
01244 a_handler)
01245 {
01246 struct CRPseudoClassSelHandlerEntry *handler_entry = NULL;
01247 GList *list = NULL;
01248
01249 g_return_val_if_fail (a_this && PRIVATE (a_this)
01250 && a_handler && a_name, CR_BAD_PARAM_ERROR);
01251
01252 handler_entry = g_try_malloc
01253 (sizeof (struct CRPseudoClassSelHandlerEntry));
01254 if (!handler_entry) {
01255 return CR_OUT_OF_MEMORY_ERROR;
01256 }
01257 memset (handler_entry, 0,
01258 sizeof (struct CRPseudoClassSelHandlerEntry));
01259 handler_entry->name = g_strdup (a_name);
01260 handler_entry->type = a_type;
01261 handler_entry->handler = a_handler;
01262 list = g_list_append (PRIVATE (a_this)->pcs_handlers, handler_entry);
01263 if (!list) {
01264 return CR_OUT_OF_MEMORY_ERROR;
01265 }
01266 PRIVATE (a_this)->pcs_handlers = list;
01267 return CR_OK;
01268 }
01269
01270 enum CRStatus
01271 cr_sel_eng_unregister_pseudo_class_sel_handler (CRSelEng * a_this,
01272 guchar * a_name,
01273 enum CRPseudoType a_type)
01274 {
01275 GList *elem = NULL,
01276 *deleted_elem = NULL;
01277 gboolean found = FALSE;
01278 struct CRPseudoClassSelHandlerEntry *entry = NULL;
01279
01280 g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
01281
01282 for (elem = PRIVATE (a_this)->pcs_handlers;
01283 elem; elem = g_list_next (elem)) {
01284 entry = elem->data;
01285 if (!strcmp (entry->name, a_name)
01286 && entry->type == a_type) {
01287 found = TRUE;
01288 break;
01289 }
01290 }
01291 if (found == FALSE)
01292 return CR_PSEUDO_CLASS_SEL_HANDLER_NOT_FOUND_ERROR;
01293 PRIVATE (a_this)->pcs_handlers = g_list_delete_link
01294 (PRIVATE (a_this)->pcs_handlers, elem);
01295 entry = elem->data;
01296 if (entry->name)
01297 g_free (entry->name);
01298 g_free (elem);
01299 g_list_free (deleted_elem);
01300
01301 return CR_OK;
01302 }
01303
01304
01305
01306
01307
01308
01309
01310
01311 enum CRStatus
01312 cr_sel_eng_unregister_all_pseudo_class_sel_handlers (CRSelEng * a_this)
01313 {
01314 GList *elem = NULL;
01315 struct CRPseudoClassSelHandlerEntry *entry = NULL;
01316
01317 g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
01318
01319 if (!PRIVATE (a_this)->pcs_handlers)
01320 return CR_OK;
01321 for (elem = PRIVATE (a_this)->pcs_handlers;
01322 elem; elem = g_list_next (elem)) {
01323 entry = elem->data;
01324 if (!entry)
01325 continue;
01326 if (entry->name) {
01327 g_free (entry->name);
01328 entry->name = NULL;
01329 }
01330 g_free (entry);
01331 elem->data = NULL;
01332 }
01333 g_list_free (PRIVATE (a_this)->pcs_handlers);
01334 PRIVATE (a_this)->pcs_handlers = NULL;
01335 return CR_OK;
01336 }
01337
01338 enum CRStatus
01339 cr_sel_eng_get_pseudo_class_selector_handler (CRSelEng * a_this,
01340 guchar * a_name,
01341 enum CRPseudoType a_type,
01342 CRPseudoClassSelectorHandler *
01343 a_handler)
01344 {
01345 GList *elem = NULL;
01346 struct CRPseudoClassSelHandlerEntry *entry = NULL;
01347 gboolean found = FALSE;
01348
01349 g_return_val_if_fail (a_this && PRIVATE (a_this)
01350 && a_name, CR_BAD_PARAM_ERROR);
01351
01352 for (elem = PRIVATE (a_this)->pcs_handlers;
01353 elem; elem = g_list_next (elem)) {
01354 entry = elem->data;
01355 if (!strcmp (a_name, entry->name)
01356 && entry->type == a_type) {
01357 found = TRUE;
01358 break;
01359 }
01360 }
01361
01362 if (found == FALSE)
01363 return CR_PSEUDO_CLASS_SEL_HANDLER_NOT_FOUND_ERROR;
01364 *a_handler = entry->handler;
01365 return CR_OK;
01366 }
01367
01368
01369
01370
01371
01372
01373
01374
01375
01376
01377
01378
01379
01380
01381 enum CRStatus
01382 cr_sel_eng_matches_node (CRSelEng * a_this, CRSimpleSel * a_sel,
01383 xmlNode * a_node, gboolean * a_result)
01384 {
01385 g_return_val_if_fail (a_this && PRIVATE (a_this)
01386 && a_this && a_node
01387 && a_result, CR_BAD_PARAM_ERROR);
01388
01389 if (a_node->type != XML_ELEMENT_NODE) {
01390 *a_result = FALSE;
01391 return CR_OK;
01392 }
01393
01394 return sel_matches_node_real (a_this, a_sel, a_node, a_result, TRUE);
01395 }
01396
01397
01398
01399
01400
01401
01402
01403
01404
01405
01406
01407
01408
01409
01410
01411
01412
01413 enum CRStatus
01414 cr_sel_eng_get_matched_rulesets (CRSelEng * a_this,
01415 CRStyleSheet * a_sheet,
01416 xmlNode * a_node,
01417 CRStatement *** a_rulesets, gulong * a_len)
01418 {
01419 CRStatement **stmts_tab = NULL;
01420 enum CRStatus status = CR_OK;
01421 gulong tab_size = 0,
01422 tab_len = 0,
01423 index = 0;
01424 gushort stmts_chunck_size = 8;
01425
01426 g_return_val_if_fail (a_this
01427 && a_sheet
01428 && a_node
01429 && a_rulesets && *a_rulesets == NULL
01430 && a_len, CR_BAD_PARAM_ERROR);
01431
01432 stmts_tab = g_try_malloc (stmts_chunck_size * sizeof (CRStatement *));
01433
01434 if (!stmts_tab) {
01435 cr_utils_trace_info ("Out of memory");
01436 status = CR_ERROR;
01437 goto error;
01438 }
01439 memset (stmts_tab, 0, stmts_chunck_size * sizeof (CRStatement *));
01440
01441 tab_size = stmts_chunck_size;
01442 tab_len = tab_size;
01443
01444 while ((status = cr_sel_eng_get_matched_rulesets_real
01445 (a_this, a_sheet, a_node, stmts_tab + index, &tab_len))
01446 == CR_OUTPUT_TOO_SHORT_ERROR) {
01447 stmts_tab = g_try_realloc (stmts_tab,
01448 (tab_size + stmts_chunck_size)
01449 * sizeof (CRStatement *));
01450 if (!stmts_tab) {
01451 cr_utils_trace_info ("Out of memory");
01452 status = CR_ERROR;
01453 goto error;
01454 }
01455 tab_size += stmts_chunck_size;
01456 index += tab_len;
01457 tab_len = tab_size - index;
01458 }
01459
01460 tab_len = tab_size - stmts_chunck_size + tab_len;
01461 *a_rulesets = stmts_tab;
01462 *a_len = tab_len;
01463
01464 return CR_OK;
01465
01466 error:
01467
01468 if (stmts_tab) {
01469 g_free (stmts_tab);
01470 stmts_tab = NULL;
01471
01472 }
01473
01474 *a_len = 0;
01475 return status;
01476 }
01477
01478 #ifndef NEW_PROPERTIES_GETTER
01479 enum CRStatus
01480 cr_sel_eng_get_matched_properties_from_cascade (CRSelEng * a_this,
01481 CRCascade * a_cascade,
01482 xmlNode * a_node,
01483 GHashTable **
01484 a_props_hashtable)
01485 {
01486 CRStatement **stmts_tab = NULL;
01487 enum CRStatus status = CR_OK;
01488 gulong tab_size = 0,
01489 tab_len = 0,
01490 i = 0,
01491 index = 0;
01492 enum CRStyleOrigin origin = 0;
01493 gushort stmts_chunck_size = 8;
01494 CRStyleSheet *sheet = NULL;
01495
01496 g_return_val_if_fail (a_this
01497 && a_cascade
01498 && a_node
01499 && a_props_hashtable, CR_BAD_PARAM_ERROR);
01500
01501 for (origin = ORIGIN_UA; origin < NB_ORIGINS; origin++) {
01502 sheet = cr_cascade_get_sheet (a_cascade, origin);
01503 if (!sheet)
01504 continue;
01505 if (tab_size - index < 1) {
01506 stmts_tab = g_try_realloc
01507 (stmts_tab, (tab_size + stmts_chunck_size)
01508 * sizeof (CRStatement *));
01509 if (!stmts_tab) {
01510 cr_utils_trace_info ("Out of memory");
01511 status = CR_ERROR;
01512 goto error;
01513 }
01514 tab_size += stmts_chunck_size;
01515
01516
01517
01518
01519 tab_len = tab_size - index;
01520 }
01521 while ((status = cr_sel_eng_get_matched_rulesets_real
01522 (a_this, sheet, a_node, stmts_tab + index, &tab_len))
01523 == CR_OUTPUT_TOO_SHORT_ERROR) {
01524 stmts_tab = g_try_realloc
01525 (stmts_tab, (tab_size + stmts_chunck_size)
01526 * sizeof (CRStatement *));
01527 if (!stmts_tab) {
01528 cr_utils_trace_info ("Out of memory");
01529 status = CR_ERROR;
01530 goto error;
01531 }
01532 tab_size += stmts_chunck_size;
01533 index += tab_len;
01534
01535
01536
01537
01538 tab_len = tab_size - index;
01539 }
01540 if (status != CR_OK) {
01541 cr_utils_trace_info ("Error while running "
01542 "selector engine");
01543 goto error;
01544 }
01545 index += tab_len;
01546 tab_len = tab_size - index;
01547 }
01548
01549
01550
01551
01552
01553
01554
01555 for (i = 0; i < index; i++) {
01556 CRStatement *stmt = stmts_tab[i];
01557
01558 if (!stmt)
01559 continue;
01560 switch (stmt->type) {
01561 case RULESET_STMT:
01562 if (!stmt->parent_sheet)
01563 continue;
01564 status = put_css_properties_in_hashtable
01565 (a_props_hashtable, stmt);
01566 break;
01567 default:
01568 break;
01569 }
01570
01571 }
01572
01573 return CR_OK;
01574 error:
01575
01576 if (stmts_tab) {
01577 g_free (stmts_tab);
01578 stmts_tab = NULL;
01579
01580 }
01581
01582 return status;
01583 }
01584
01585 #else
01586 enum CRStatus
01587 cr_sel_eng_get_matched_properties_from_cascade (CRSelEng * a_this,
01588 CRCascade * a_cascade,
01589 xmlNode * a_node,
01590 CRPropList ** a_props)
01591 {
01592 CRStatement **stmts_tab = NULL;
01593 enum CRStatus status = CR_OK;
01594 gulong tab_size = 0,
01595 tab_len = 0,
01596 i = 0,
01597 index = 0;
01598 enum CRStyleOrigin origin = 0;
01599 gushort stmts_chunck_size = 8;
01600 CRStyleSheet *sheet = NULL;
01601
01602 g_return_val_if_fail (a_this
01603 && a_cascade
01604 && a_node && a_props, CR_BAD_PARAM_ERROR);
01605
01606 for (origin = ORIGIN_UA; origin < NB_ORIGINS; origin++) {
01607 sheet = cr_cascade_get_sheet (a_cascade, origin);
01608 if (!sheet)
01609 continue;
01610 if (tab_size - index < 1) {
01611 stmts_tab = g_try_realloc
01612 (stmts_tab, (tab_size + stmts_chunck_size)
01613 * sizeof (CRStatement *));
01614 if (!stmts_tab) {
01615 cr_utils_trace_info ("Out of memory");
01616 status = CR_ERROR;
01617 goto cleanup;
01618 }
01619 tab_size += stmts_chunck_size;
01620
01621
01622
01623
01624 tab_len = tab_size - index;
01625 }
01626 while ((status = cr_sel_eng_get_matched_rulesets_real
01627 (a_this, sheet, a_node, stmts_tab + index, &tab_len))
01628 == CR_OUTPUT_TOO_SHORT_ERROR) {
01629 stmts_tab = g_try_realloc
01630 (stmts_tab, (tab_size + stmts_chunck_size)
01631 * sizeof (CRStatement *));
01632 if (!stmts_tab) {
01633 cr_utils_trace_info ("Out of memory");
01634 status = CR_ERROR;
01635 goto cleanup;
01636 }
01637 tab_size += stmts_chunck_size;
01638 index += tab_len;
01639
01640
01641
01642
01643 tab_len = tab_size - index;
01644 }
01645 if (status != CR_OK) {
01646 cr_utils_trace_info ("Error while running "
01647 "selector engine");
01648 goto cleanup;
01649 }
01650 index += tab_len;
01651 tab_len = tab_size - index;
01652 }
01653
01654
01655
01656
01657
01658
01659
01660 for (i = 0; i < index; i++) {
01661 CRStatement *stmt = stmts_tab[i];
01662
01663 if (!stmt)
01664 continue;
01665 switch (stmt->type) {
01666 case RULESET_STMT:
01667 if (!stmt->parent_sheet)
01668 continue;
01669 status = put_css_properties_in_props_list
01670 (a_props, stmt);
01671 break;
01672 default:
01673 break;
01674 }
01675
01676 }
01677 status = CR_OK ;
01678 cleanup:
01679 if (stmts_tab) {
01680 g_free (stmts_tab);
01681 stmts_tab = NULL;
01682 }
01683
01684 return status;
01685 }
01686 #endif
01687
01688 enum CRStatus
01689 cr_sel_eng_get_matched_style (CRSelEng * a_this,
01690 CRCascade * a_cascade,
01691 xmlNode * a_node,
01692 CRStyle * a_parent_style, CRStyle ** a_style)
01693 {
01694 enum CRStatus status = CR_OK;
01695
01696 #ifndef NEW_PROPERTIES_GETTER
01697 GHashTable *props_hash = NULL;
01698 #else
01699 CRPropList *props = NULL;
01700 #endif
01701
01702 g_return_val_if_fail (a_this && a_cascade
01703 && a_node && a_style, CR_BAD_PARAM_ERROR);
01704 #ifndef NEW_PROPERTIES_GETTER
01705 status = cr_sel_eng_get_matched_properties_from_cascade
01706 (a_this, a_cascade, a_node, &props_hash);
01707 #else
01708 status = cr_sel_eng_get_matched_properties_from_cascade
01709 (a_this, a_cascade, a_node, &props);
01710 #endif
01711 g_return_val_if_fail (status == CR_OK, status);
01712
01713 #ifndef NEW_PROPERTIES_GETTER
01714 if (props_hash && g_hash_table_size (props_hash)) {
01715
01716 if (!*a_style) {
01717 *a_style = cr_style_new ();
01718 g_return_val_if_fail (*a_style, CR_ERROR);
01719 } else {
01720 cr_style_set_props_to_defaults (*a_style);
01721 }
01722 (*a_style)->parent_style = a_parent_style;
01723
01724 g_hash_table_foreach (props_hash, ((GHFunc)
01725 set_style_from_props_hash_hr_func),
01726 *a_style);
01727 }
01728 if (props_hash) {
01729 g_hash_table_destroy (props_hash);
01730 props_hash = NULL;
01731 }
01732 #else
01733 if (props) {
01734
01735 if (!*a_style) {
01736 *a_style = cr_style_new ();
01737 g_return_val_if_fail (*a_style, CR_ERROR);
01738 } else {
01739 cr_style_set_props_to_defaults (*a_style);
01740 }
01741 (*a_style)->parent_style = a_parent_style;
01742
01743 set_style_from_props (*a_style, props);
01744 if (props) {
01745 cr_prop_list_destroy (props);
01746 props = NULL;
01747 }
01748 }
01749 #endif
01750 return CR_OK;
01751 }
01752
01753
01754
01755
01756
01757 void
01758 cr_sel_eng_destroy (CRSelEng * a_this)
01759 {
01760 g_return_if_fail (a_this);
01761
01762 if (!PRIVATE (a_this))
01763 goto end ;
01764 if (PRIVATE (a_this)->pcs_handlers) {
01765 cr_sel_eng_unregister_all_pseudo_class_sel_handlers
01766 (a_this) ;
01767 PRIVATE (a_this)->pcs_handlers = NULL ;
01768 }
01769 g_free (PRIVATE (a_this));
01770 PRIVATE (a_this) = NULL;
01771 end:
01772 if (a_this) {
01773 g_free (a_this);
01774 }
01775 }