OSDN Git Service

* changed feature.
[modchxj/mod_chxj.git] / src / chxj_css.c
1 /*
2  * Copyright (C) 2005-2011 Atsushi Konno All rights reserved.
3  * Copyright (C) 2005 QSDN,Inc. All rights reserved.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 #include "chxj_css.h"
18 #include "chxj_serf.h"
19 #include "chxj_encoding.h"
20 #include "chxj_str_util.h"
21 #include "qs_parse_string.h"
22 #include "apr_pools.h"
23 #include "scss.h"
24
25 #include <libgen.h>
26
27 #if defined(CHXJ_TEST)
28 #undef ap_regex_t
29 #undef ap_regmatch_t
30 #endif
31
32 #undef list_insert
33 #undef list_remove
34 #define list_insert(node, point) do {           \
35     node->ref  = point->ref;                    \
36     *node->ref = node;                          \
37     node->next = point;                         \
38     point->ref = &node->next;                   \
39 } while (0)
40
41 #define list_remove(node) do {                  \
42     *node->ref      = node->next;               \
43     node->next->ref = node->ref;                \
44 } while (0)
45
46 /*===========================================================================*/
47 /* PARSER                                                                    */
48 /*===========================================================================*/
49 struct css_already_import_stack {
50   char *full_url;
51   struct css_already_import_stack *next;
52   struct css_already_import_stack **ref;
53 };
54
55 struct css_app_data {
56   css_stylesheet_t *stylesheet;
57   char **selector_list;
58   int selector_count;
59   apr_pool_t *pool;
60   request_rec *r;
61   int error_occured;
62   css_property_t property_head;
63   struct css_already_import_stack imported_stack_head;
64 };
65
66 /*===========================================================================*/
67 /*                                                                           */
68 /* PROTOTYPE                                                                 */
69 /*                                                                           */
70 /*===========================================================================*/
71 static void s_css_parser_from_uri_start_selector(SCSSParserPtr_t parser, SCSSNodePtr_t selectors);
72 static void s_css_parser_from_uri_end_selector(SCSSParserPtr_t parser, SCSSNodePtr_t selectors);
73 static void s_css_parser_from_uri_property(SCSSParserPtr_t parser, const char *propertyName, const char *value, int impotant);
74 static css_property_t *s_css_parser_copy_property(apr_pool_t *pool, css_property_t *from);
75 static css_selector_t *s_new_selector(apr_pool_t *pool, css_stylesheet_t *stylesheet, char *name);
76 static css_selector_t *s_search_selector(css_stylesheet_t *stylesheet, const char *name);
77 static void s_merge_property(css_selector_t *sel, css_property_t *tgt);
78 static void s_css_parser_from_uri_import_style(SCSSParserPtr_t parser, const char *uri, const char **media, const char *defaultNamespaceURI);
79 static char *s_path_to_fullurl(apr_pool_t *pool, const char *base_url, const char *base_path, const char *uri);
80 static char *s_uri_to_base_url(apr_uri_t *uri, apr_pool_t *pool);
81 static css_stylesheet_t *s_chxj_css_parse_from_uri(request_rec *r, apr_pool_t *pool, struct css_already_import_stack *imported_stack, css_stylesheet_t *old_stylesheet, const char *uri);
82 static int s_is_already_imported(struct css_already_import_stack *imported_stack_head, const char *url);
83 static css_stylesheet_t *s_merge_stylesheet(apr_pool_t *pool, css_stylesheet_t *old_stylesheet, css_stylesheet_t *new_stylesheet);
84 static void s_copy_already_import_stack(apr_pool_t *pool, struct css_already_import_stack *base, struct css_already_import_stack *imported_stack);
85 static css_selector_t *s_search_selector_regexp(Doc *doc, request_rec *r, apr_pool_t *pool, css_stylesheet_t *stylesheet, const char *pattern_str1, const char *pattern_str2, Node *node);
86 static void s_get_tag_and_class_and_id(Doc *doc, Node *node, char **tag_name, char **class_name, char **id);
87 static char *s_cmp_now_node_vs_current_style(Doc *doc, request_rec *r, apr_pool_t *pool, char *src, ap_regex_t *pattern4, Node *node);
88 static css_stylesheet_t *s_dup_stylesheet(Doc *doc, css_stylesheet_t *org);
89
90
91 /**
92  * Data is acquired from url specified by using libserf. 
93  * The acquired data is analyzed in the syntax and css_stylesheet_t is made. 
94  *
95  * @param r request_rec
96  * @param pool pool
97  * @param old_stylesheet old stylesheet
98  * @param uri url
99  *
100  * @return css_stylesheet_t
101  */
102 css_stylesheet_t *
103 chxj_css_parse_from_uri(request_rec *r, apr_pool_t *pool, css_stylesheet_t *old_stylesheet, const char *uri)
104 {
105   return s_chxj_css_parse_from_uri(r, pool, NULL, old_stylesheet, uri);
106 }
107
108 /**
109  * find selector engine.
110  * @param doc        Doc structure.
111  * @param stylesheet Retrieval object.
112  * @param node       this node.
113  * @return css_selector_t if any. null if not found.
114  *
115  */
116 css_selector_t *
117 chxj_css_find_selector(Doc *doc, css_stylesheet_t *stylesheet, Node *node)
118 {
119   request_rec *r    = doc->r;
120   apr_pool_t  *pool = doc->pool;
121   css_selector_t *sel = NULL;
122   char *tag_name   = NULL;
123   char *class_name = NULL;
124   char *id         = NULL;
125   DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
126
127   if (! stylesheet) {
128     /* NOT FOUND */
129     return NULL;
130   }
131   s_get_tag_and_class_and_id(doc, node, &tag_name, &class_name, &id);
132   
133   if (! tag_name || strcasecmp("ROOT", tag_name) == 0) {
134     ERR(r,"REQ[%X] %s:%d tag_name is null", TO_ADDR(r),APLOG_MARK);
135     return NULL;
136   }
137   char *pattern_str1 = NULL;
138   char *pattern_str2 = NULL;
139   if (class_name && id) {
140     pattern_str1 = apr_psprintf(pool, 
141                                 "^((%s|\\*)(\\.%s)?(#%s)?|(\\*|)\\.%s(#%s)?|(\\*|)(\\.%s)?#%s)$",
142                                 tag_name,
143                                 class_name,
144                                 id,
145                                 class_name,
146                                 id,
147                                 class_name,
148                                 id);
149     pattern_str2 = apr_psprintf(pool,
150                                 ".*([ >+])((%s|\\*)(\\.%s)?(#%s)?|(\\*|)\\.%s(#%s)?|(\\*|)(\\.%s)?#%s)$",
151                                 tag_name,
152                                 class_name,
153                                 id,
154                                 class_name,
155                                 id,
156                                 class_name,
157                                 id);
158     sel = s_search_selector_regexp(doc, r, pool, stylesheet, pattern_str1, pattern_str2, node);
159     if (sel) {
160       DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
161       return sel;
162     }
163   }
164   else if (id) {
165     pattern_str1 = apr_psprintf(pool, 
166                                 "^((%s|\\*)(#%s)?|(\\*|)(#%s)?|(\\*|)#%s)$",
167                                 tag_name,
168                                 id,
169                                 id,
170                                 id);
171     pattern_str2 = apr_psprintf(pool,
172                                 ".*([ >+])((%s|\\*)(#%s)?|(\\*|)(#%s)?|(\\*|)#%s)$",
173                                 tag_name,
174                                 id,
175                                 id,
176                                 id);
177     sel = s_search_selector_regexp(doc, r, pool, stylesheet, pattern_str1, pattern_str2, node);
178     if (sel) {
179       DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
180       return sel;
181     }
182   }
183   else if (class_name) {
184     pattern_str1 = apr_psprintf(pool, 
185                                 "^((%s|\\*)(\\.%s)?|(\\*|)\\.%s|(\\*|)(\\.%s))$",
186                                 tag_name,
187                                 class_name,
188                                 class_name,
189                                 class_name);
190     pattern_str2 = apr_psprintf(pool,
191                                 ".*([ >+])((%s|\\*)(\\.%s)?|(\\*|)\\.%s|(\\*|)(\\.%s)?)$",
192                                 tag_name,
193                                 class_name,
194                                 class_name,
195                                 class_name);
196     sel = s_search_selector_regexp(doc, r, pool, stylesheet, pattern_str1, pattern_str2, node);
197     if (sel) {
198       DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
199       return sel;
200     }
201   }
202   else {
203     pattern_str1 = apr_psprintf(pool, 
204                                 "^(%s|\\*)$",
205                                 tag_name);
206     pattern_str2 = apr_psprintf(pool,
207                                 ".*([ >+])(%s|\\*)$",
208                                 tag_name);
209     sel = s_search_selector_regexp(doc, r, pool, stylesheet, pattern_str1, pattern_str2, node);
210     if (sel) {
211       DBG(r,"REQ[%X] (FOUND)",TO_ADDR(r));
212       DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
213       return sel;
214     }
215   }
216
217   DBG(r,"REQ[%X] (Not FOUND)",TO_ADDR(r));
218   DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
219   return sel;
220 }
221
222 /*===========================================================================*/
223 /*                                                                           */
224 /* STATIC                                                                    */
225 /*                                                                           */
226 /*===========================================================================*/
227 static css_selector_t *
228 s_search_selector_regexp(Doc *doc, request_rec *r, apr_pool_t *pool, css_stylesheet_t *stylesheet, const char *pattern_str1, const char *pattern_str2, Node *node)
229 {
230   Node *node_sv = node;
231   css_selector_t *ret_sel = NULL;
232   css_selector_t *tail;
233   css_selector_t *cur;
234   ap_regex_t *pattern1 = chxj_ap_pregcomp(pool, pattern_str1, AP_REG_EXTENDED|AP_REG_ICASE);
235   ap_regex_t *pattern2 = chxj_ap_pregcomp(pool, pattern_str2, AP_REG_EXTENDED|AP_REG_ICASE);
236   ap_regex_t *pattern3 = chxj_ap_pregcomp(pool, "^.*([>+ ])([^>+ ]+?)$", AP_REG_EXTENDED|AP_REG_ICASE);
237   ap_regex_t *pattern4 = chxj_ap_pregcomp(pool, "^([^.#]+?)?(\\.[^#]+?)?(#.+?)?$", AP_REG_EXTENDED|AP_REG_ICASE);
238                               
239   tail = (css_selector_t *)((apr_size_t)stylesheet->selector_head.ref - (apr_size_t)APR_OFFSETOF(css_selector_t, next));
240   for (cur = tail; 
241        cur != &stylesheet->selector_head; 
242        cur = (css_selector_t *)((apr_size_t)cur->ref - (apr_size_t)APR_OFFSETOF(css_selector_t, next))) {
243     ap_regmatch_t match[256];
244     if (chxj_ap_regexec(pattern1, cur->name, pattern1->re_nsub + 1, match, 0) == 0) {
245       ret_sel = cur;
246       goto end_of_search;
247     }
248     else 
249     if (chxj_ap_regexec(pattern2, cur->name, pattern2->re_nsub + 1, match, 0) == 0) {
250       char *src = apr_pstrdup(pool, cur->name);
251       char *one = chxj_ap_pregsub(pool, "$1",src, pattern2->re_nsub + 1, match);
252       int loop = 0;
253       do {
254         *strrchr(src, *one) = 0;
255         switch (*one) {
256         case '>': /* Child selectors */
257           if (chxj_ap_regexec(pattern3, src, pattern3->re_nsub + 1, match, 0) == 0) {
258             one = chxj_ap_pregsub(pool, "$1",src, pattern3->re_nsub + 1, match);
259
260             char *ret = s_cmp_now_node_vs_current_style(doc, r, pool, strrchr(src, *one)+1, pattern4, node->parent);
261             if (ret) {
262               node = node->parent;
263               loop = 1;
264             }
265           }
266           else {
267             char *ret = s_cmp_now_node_vs_current_style(doc, r, pool, src, pattern4, node->parent);
268             if (ret) {
269               ret_sel = cur;
270               goto end_of_search;
271             }
272             loop = 0;
273           }
274           break;
275
276
277         case '+': /* Adjacent sibling selectors */
278           if (chxj_ap_regexec(pattern3, src, pattern3->re_nsub + 1, match, 0) == 0) {
279             one = chxj_ap_pregsub(pool, "$1",src, pattern3->re_nsub + 1, match);
280             char *ret = s_cmp_now_node_vs_current_style(doc, r, pool, strrchr(src, *one)+1, pattern4, node->prev);
281             if (ret) {
282               loop = 1;
283               node = node->prev;
284               break;
285             }
286           }
287           else {
288             if (! node->prev) {
289               ret_sel = NULL;
290               goto end_of_search;
291             }
292             char *ret = s_cmp_now_node_vs_current_style(doc, r, pool, src, pattern4, node->prev);
293             if (ret) {
294               ret_sel = cur;
295               goto end_of_search;
296             }
297           }
298           break;
299
300
301         case ' ': /* Descendant selectors */
302           if (chxj_ap_regexec(pattern3, src, pattern3->re_nsub + 1, match, 0) == 0) {
303             one = chxj_ap_pregsub(pool, "$1",src, pattern3->re_nsub + 1, match);
304             for (; node && node->parent; node = node->parent) {
305               if (strcasecmp(node->name, "ROOT") == 0 || strcasecmp(node->parent->name, "ROOT") == 0) {
306                 loop = 0;
307                 break;
308               }
309               char *ret = s_cmp_now_node_vs_current_style(doc, r, pool, strrchr(src, *one)+1, pattern4, node->parent);
310               if (ret) {
311                 loop = 1;
312                 node = node->parent;
313                 break;
314               }
315             }
316           }
317           else {
318             for (; node && node->parent; node = node->parent) {
319               if (strcasecmp(node->name, "ROOT") == 0 || strcasecmp(node->parent->name, "ROOT") == 0) {
320                 loop = 0;
321                 break;
322               }
323               char *ret = s_cmp_now_node_vs_current_style(doc, r, pool, src, pattern4, node->parent);
324               if (ret) {
325                 ret_sel = cur;
326                 goto end_of_search;
327               }
328             }
329           }
330           break;
331         default:
332           loop = 0;
333         }
334       } while(loop);
335       node = node_sv;
336     }
337   }
338
339 end_of_search:
340   chxj_ap_pregfree(pool, pattern1);
341   chxj_ap_pregfree(pool, pattern2);
342   chxj_ap_pregfree(pool, pattern3);
343   chxj_ap_pregfree(pool, pattern4);
344   return ret_sel;
345 }
346 static css_stylesheet_t *
347 s_chxj_css_parse_from_uri(request_rec *r, apr_pool_t *pool, struct css_already_import_stack *imported_stack, css_stylesheet_t *old_stylesheet, const char *uri)
348 {
349   SCSSParserPtr_t     parser  = NULL;
350   SCSSSACHandlerPtr_t handler = NULL;
351   char         *css         = NULL;
352   char         *full_url    = NULL;
353   apr_size_t  srclen;
354   css_stylesheet_t *stylesheet = NULL;
355   struct css_already_import_stack *new_stack;
356   struct css_app_data app_data;
357   char         *base_url;
358   apr_size_t   css_len;
359   
360
361   DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
362   DBG(r,"REQ[%X] uri:[%s]", TO_ADDR(r),uri);
363
364   base_url = s_uri_to_base_url(&r->parsed_uri, pool);
365   full_url = s_path_to_fullurl(pool, base_url, r->parsed_uri.path, uri);
366
367   /* check already import */
368   if (imported_stack && s_is_already_imported(imported_stack, full_url)) {
369     DBG(r,"REQ[%X] already imported:[%s]", TO_ADDR(r),full_url); 
370     DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
371     return NULL;
372   }
373
374   /* GET request */
375   css = chxj_serf_get(r, pool, full_url, 0, &css_len);
376   if (css == NULL) {
377     ERR(r,"REQ[%X] %s:%d end chxj_css_parse_from_uri(): serf_get failed: url:[%s]", TO_ADDR(r),APLOG_MARK, uri);
378     DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
379     return NULL;
380   }
381   srclen = strlen(css);
382   
383   /* create parser */
384   parser = scss_parser_new_from_buf(pool, css, "");
385   if (!parser) {
386     ERR(r,"REQ[%X] %s:%d end chxj_css_parse_from_uri(): cr_parser_new_from_buf() failed", TO_ADDR(r),APLOG_MARK);
387     return NULL;
388   }
389
390   /* create handler */
391   handler = scss_doc_handler_new(parser);
392   if (!handler) {
393     ERR(r, "REQ[%X] %s:%d end chxj_css_parse_from_uri(): cr_doc_handler_new() failed", TO_ADDR(r),APLOG_MARK);
394     return NULL;
395   }
396
397   stylesheet = apr_palloc(pool, sizeof(*stylesheet));
398   memset(stylesheet, 0, sizeof(*stylesheet));
399   stylesheet->selector_head.next = &stylesheet->selector_head;
400   stylesheet->selector_head.ref  = &stylesheet->selector_head.next;
401
402   memset(&app_data, 0, sizeof(struct css_app_data));
403   app_data.stylesheet     = stylesheet;
404   app_data.selector_list  = NULL;
405   app_data.selector_count = 0;
406   app_data.pool           = pool;
407   app_data.error_occured  = 0;
408   app_data.r              = r;
409   if (imported_stack) {
410     s_copy_already_import_stack(pool, &app_data.imported_stack_head, imported_stack);
411   }
412   else {
413     app_data.imported_stack_head.next = &app_data.imported_stack_head;
414     app_data.imported_stack_head.ref  = &app_data.imported_stack_head.next;
415   }
416   new_stack = apr_palloc(pool, sizeof(*new_stack));
417   memset(new_stack, 0, sizeof(*new_stack));
418   new_stack->next = new_stack;
419   new_stack->ref  = &new_stack->next;
420   new_stack->full_url = full_url;
421   list_insert(new_stack, (&app_data.imported_stack_head));
422
423   scss_doc_set_user_data(parser->doc, &app_data);
424
425   handler->startSelector = s_css_parser_from_uri_start_selector;
426   handler->endSelector   = s_css_parser_from_uri_end_selector;
427   handler->property      = s_css_parser_from_uri_property;
428   handler->import        = s_css_parser_from_uri_import_style;
429
430   scss_parse_stylesheet(parser);
431   DBG(r,"REQ[%X] url:[%s]", TO_ADDR(r),uri);
432   DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
433
434   return s_merge_stylesheet(pool, old_stylesheet, app_data.stylesheet);
435 }
436
437 static css_stylesheet_t *
438 s_chxj_css_parse_from_buf(request_rec *r, apr_pool_t *pool, struct css_already_import_stack *imported_stack, css_stylesheet_t *old_stylesheet, const char *css)
439 {
440   apr_size_t  srclen;
441   SCSSParserPtr_t     parser  = NULL;
442   SCSSSACHandlerPtr_t handler = NULL;
443   css_stylesheet_t *stylesheet = NULL;
444   struct css_app_data app_data;
445   struct css_already_import_stack *new_stack;
446
447   DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
448   DBG(r,"REQ[%X] css:[%s]", TO_ADDR(r),css);
449   srclen = strlen(css);
450   
451   /* create parser */
452   parser = scss_parser_new_from_buf(pool, css, "");
453   if (!parser) {
454     ERR(r,"REQ[%X] %s:%d end chxj_css_parse_from_uri(): scss_parser_new_from_buf() failed", TO_ADDR(r),APLOG_MARK);
455     return NULL;
456   }
457   handler = scss_doc_handler_new(parser);
458   if (!handler) {
459     ERR(r,"REQ[%X] %s:%d end chxj_css_parse_from_uri(): scss_doc_handler_new() failed", TO_ADDR(r),APLOG_MARK);
460     return NULL;
461   }
462
463   stylesheet = apr_palloc(pool, sizeof(*stylesheet));
464   memset(stylesheet, 0, sizeof(*stylesheet));
465   stylesheet->selector_head.next = &stylesheet->selector_head;
466   stylesheet->selector_head.ref  = &stylesheet->selector_head.next;
467
468   memset(&app_data, 0, sizeof(struct css_app_data));
469   app_data.stylesheet     = stylesheet;
470   app_data.selector_list  = NULL;
471   app_data.selector_count = 0;
472   app_data.pool           = pool;
473   app_data.error_occured  = 0;
474   app_data.r              = r;
475   if (imported_stack) {
476     s_copy_already_import_stack(pool, &app_data.imported_stack_head, imported_stack);
477   }
478   else {
479     app_data.imported_stack_head.next = &app_data.imported_stack_head;
480     app_data.imported_stack_head.ref  = &app_data.imported_stack_head.next;
481   }
482
483   scss_doc_set_user_data(parser->doc, &app_data);
484
485   new_stack = apr_palloc(pool, sizeof(*new_stack));
486   memset(new_stack, 0, sizeof(*new_stack));
487   new_stack->next = new_stack;
488   new_stack->ref  = &new_stack->next;
489   new_stack->full_url = "";
490   list_insert(new_stack, (&app_data.imported_stack_head));
491
492   handler->startSelector = s_css_parser_from_uri_start_selector;
493   handler->endSelector   = s_css_parser_from_uri_end_selector;
494   handler->property      = s_css_parser_from_uri_property;
495   handler->import        = s_css_parser_from_uri_import_style;
496
497   scss_parse_stylesheet(parser);
498   DBG(r,"REQ[%X] css:[%s]", TO_ADDR(r),css);
499   DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
500   return s_merge_stylesheet(pool, old_stylesheet, app_data.stylesheet);
501 }
502
503
504 #define ERROR_OCCORED do {          \
505     if (app_data->error_occured) {  \
506       return;                       \
507     }                               \
508   }                                 \
509   while (0)
510
511
512 #define CB_INIT \
513   struct css_app_data *app_data = (struct css_app_data *)scss_doc_get_user_data(parser->doc)
514
515
516
517 static void 
518 s_css_parser_from_uri_start_selector(SCSSParserPtr_t parser, SCSSNodePtr_t selectors)
519 {
520   int ii;
521   SCSSNodePtr_t cur = NULL;
522   CB_INIT;
523   ERROR_OCCORED;
524
525   app_data->selector_count = 0;
526   for (cur = selectors->next; cur != selectors; cur = cur->next)
527     app_data->selector_count++;
528
529   app_data->selector_list = apr_palloc(app_data->pool, sizeof(char *) * app_data->selector_count);
530   if (! app_data->selector_list) {
531     ERR(app_data->r, "REQ[%X] %s:%d Out of memory", TO_ADDR(app_data->r),APLOG_MARK);
532     app_data->error_occured = 1;
533     return;
534   }
535   ii = 0;
536   for (cur = selectors->next; cur != selectors; cur = cur->next) {
537     app_data->selector_list[ii++] = apr_pstrdup(app_data->pool, (char *)cur->name);
538   }
539   app_data->property_head.next = &app_data->property_head;
540   app_data->property_head.ref  = &app_data->property_head.next;
541 }
542
543
544 static void 
545 s_css_parser_from_uri_end_selector(SCSSParserPtr_t parser, SCSSNodePtr_t UNUSED(selectors))
546 {
547   int ii;
548   css_property_t *cur = NULL;
549   CB_INIT;
550   ERROR_OCCORED;
551
552   if (app_data->property_head.next)  {
553     for (ii=0; ii<app_data->selector_count; ii++) {
554       css_selector_t *sel = s_new_selector(app_data->pool, app_data->stylesheet, app_data->selector_list[ii]); 
555
556       for (cur = app_data->property_head.next; cur && cur != &app_data->property_head; cur = cur->next) {
557         css_property_t *tgt = s_css_parser_copy_property(app_data->pool, cur);
558         s_merge_property(sel, tgt);
559       }
560       css_selector_t *point_selector = &app_data->stylesheet->selector_head;
561       list_insert(sel, point_selector);
562     }
563   }
564   app_data->property_head.next = &app_data->property_head;
565   app_data->property_head.ref  = &app_data->property_head.next;
566 }
567
568 static void
569 s_merge_property(css_selector_t *sel, css_property_t *tgt)
570 {
571   css_property_t *cur;
572   css_property_t *pnt = &sel->property_head;
573   char l = tolower(*tgt->name);
574   char u = toupper(*tgt->name);
575   for (cur = pnt->next; cur != pnt;cur = cur->next) {
576     if ((l == *cur->name || u == *cur->name) && strcasecmp(cur->name, tgt->name) == 0) {
577       cur->value = tgt->value;
578       return;
579     }
580   }
581   list_insert(tgt,  pnt);
582 }
583
584 static css_selector_t *
585 s_new_selector(apr_pool_t *pool, css_stylesheet_t *stylesheet, char *name)
586 {
587   css_selector_t *sel = NULL;
588   sel = s_search_selector(stylesheet, name);
589   if (sel) {
590     list_remove(sel);
591     sel->next = sel;
592     sel->ref = &sel->next;
593   }
594   else {
595     sel = apr_palloc(pool, sizeof(css_selector_t));
596     memset(sel, 0, sizeof(css_selector_t));
597     sel->name = name;
598     sel->next = sel;
599     sel->ref = &sel->next;
600     sel->property_head.next = &sel->property_head;
601     sel->property_head.ref = &sel->property_head.next;
602   }
603   return sel;
604 }
605
606 static css_selector_t *
607 s_search_selector(css_stylesheet_t *stylesheet, const char *name)
608 {
609   css_selector_t *cur;
610   char l = tolower(*name);
611   char u = toupper(*name);
612   if (! stylesheet) return NULL;
613   for (cur = stylesheet->selector_head.next; cur != &stylesheet->selector_head; cur = cur->next) {
614     if (cur->name && (l == *cur->name || u == *cur->name) && strcasecmp(cur->name, name) == 0) {
615       return cur;
616     }
617   }
618   return NULL;
619 }
620
621 static css_property_t *
622 s_css_parser_copy_property(apr_pool_t *pool, css_property_t *from)
623 {
624   css_property_t *prop = apr_palloc(pool, sizeof(css_property_t));
625   prop->name  = apr_pstrdup(pool, from->name);
626   prop->value = apr_pstrdup(pool, from->value);
627   prop->next  = prop;
628   prop->ref   = &prop->next;
629   return prop;
630 }
631
632
633
634
635 static void
636 s_css_parser_from_uri_property(SCSSParserPtr_t parser, const char *propertyName, const char *value, int UNUSED(impotant))
637 {
638   CB_INIT;
639   ERROR_OCCORED;
640   css_property_t *property;
641
642   if (propertyName && value) {
643     property = apr_palloc(app_data->pool, sizeof(*property));
644     memset(property, 0, sizeof(*property));
645     property->name = apr_pstrdup(app_data->pool, propertyName);
646     property->value = apr_pstrdup(app_data->pool, value);
647
648     css_property_t *point_property = &app_data->property_head;
649     list_insert(property, point_property);
650   }
651 }
652
653
654
655
656 static void
657 s_css_parser_from_uri_import_style(SCSSParserPtr_t parser, const char *uri, const char **media, const char *UNUSED(defaultNamespaceURI))
658 {
659   CB_INIT;
660   ERROR_OCCORED;
661   int flag = 0;
662   int ii;
663   css_stylesheet_t *new_stylesheet = NULL;
664
665   for (ii=0; ii<SCSS_MEDIA_TYPE_NUM; ii++) {
666     if (('h' == *media[ii] || 'H' == *media[ii]) && strcasecmp(media[ii], "handheld") == 0) {
667       flag = 1;
668       break;
669     }
670     if (('a' == *media[ii] || 'A' == *media[ii]) && strcasecmp(media[ii], "all") == 0) {
671       flag = 1;
672       break;
673     }
674   }
675   if (flag) {
676     if (uri) {
677       char      *new_url = NULL;
678       char      *base_url = NULL;
679
680       base_url = s_uri_to_base_url(&app_data->r->parsed_uri, app_data->pool);
681       new_url = s_path_to_fullurl(app_data->pool, base_url, app_data->r->parsed_uri.path, uri);
682       
683       new_stylesheet = s_chxj_css_parse_from_uri(app_data->r, app_data->pool, &app_data->imported_stack_head, app_data->stylesheet, new_url);
684       if (new_stylesheet) {
685         app_data->stylesheet = new_stylesheet;
686       }
687     }
688   }
689 }
690
691
692 static char *
693 s_path_to_fullurl(apr_pool_t *pool, const char *base_url, const char *base_path, const char *uri)
694 {
695   char *new_url = NULL;
696   if (chxj_starts_with(uri, "http")) {
697     return apr_pstrdup(pool, uri);
698   }
699
700   if (*uri == '/') {
701     return apr_pstrcat(pool, base_url, uri, NULL);
702   }
703
704   new_url = apr_pstrcat(pool, base_url, base_path, NULL);
705   if (new_url[strlen(new_url)-1] == '/') {
706     new_url = apr_pstrcat(pool, new_url, uri, NULL);
707   }
708   else {
709     new_url = apr_pstrcat(pool, new_url, "/", uri, NULL);
710   }
711   return new_url;
712 }
713
714
715 static char *
716 s_uri_to_base_url(apr_uri_t *uri, apr_pool_t *pool)
717 {
718   char *new_url = apr_psprintf(pool, "%s://%s", uri->scheme, uri->hostname);
719   if (strcmp(uri->scheme, "http") == 0) {
720     if (uri->port != 80 && uri->port != 0) {
721       new_url = apr_pstrcat(pool, new_url, apr_psprintf(pool, ":%d", uri->port), NULL);
722     }
723   }
724   else if (strcmp(uri->scheme, "https") == 0) {
725     if (uri->port != 443 && uri->port != 0) {
726       new_url = apr_pstrcat(pool, new_url, apr_psprintf(pool, ":%d", uri->port), NULL);
727     }
728   }
729   return new_url;
730 }
731
732 static int
733 s_is_already_imported(struct css_already_import_stack *imported_stack_head, const char *url)
734 {
735   struct css_already_import_stack *cur;
736   char l = tolower(*url);
737   char u = toupper(*url);
738   for (cur = imported_stack_head->next; cur != imported_stack_head; cur = cur->next) {
739     if ((l == *cur->full_url || u == *cur->full_url) && strcasecmp(url, cur->full_url) == 0) {
740       return 1;
741     }
742   }
743   return 0;
744 }
745
746 static css_stylesheet_t *
747 s_merge_stylesheet(apr_pool_t *pool, css_stylesheet_t *old_stylesheet, css_stylesheet_t *new_stylesheet)
748 {
749   css_selector_t *cur;
750   if (! old_stylesheet) {
751     return new_stylesheet;
752   }
753
754   for (cur = new_stylesheet->selector_head.next; cur != &new_stylesheet->selector_head; cur = cur->next) {
755     char *name = cur->name;
756     char l = tolower(*name);
757     char u = toupper(*name);
758     css_selector_t *cur_old;
759     int found;
760
761     found = 0;
762     for (cur_old = old_stylesheet->selector_head.next; cur_old != &old_stylesheet->selector_head; cur_old = cur_old->next) {
763       char *oldname = cur_old->name;
764       if ((l == *oldname || u == *oldname) && strcasecmp(name, oldname) == 0) {
765         css_property_t *cur_prop;
766         for (cur_prop = cur->property_head.next; cur_prop != &cur->property_head; cur_prop = cur_prop->next) {
767           css_property_t *target = s_css_parser_copy_property(pool, cur_prop);
768           s_merge_property(cur_old, target);
769         }
770         found = 1;
771         break;
772       }
773     }
774     if (! found) {
775       /* add new selector */
776       css_property_t *cur_prop;
777       css_selector_t *new_selector =  apr_palloc(pool, sizeof(*new_selector));
778       memset(new_selector, 0, sizeof(*new_selector));
779       new_selector->next = new_selector;
780       new_selector->ref  = &new_selector->next;
781       new_selector->property_head.next = &new_selector->property_head;
782       new_selector->property_head.ref  = &new_selector->property_head.next;
783       new_selector->name = apr_pstrdup(pool, name);
784       for (cur_prop = cur->property_head.next; cur_prop != &cur->property_head; cur_prop = cur_prop->next) {
785         css_property_t *target = s_css_parser_copy_property(pool, cur_prop);
786         list_insert(target, (&new_selector->property_head));
787       }
788       list_insert(new_selector, (&old_stylesheet->selector_head));
789     }
790   }
791
792   return old_stylesheet;
793 }
794
795
796 static void
797 s_copy_already_import_stack(apr_pool_t *pool, struct css_already_import_stack *base, struct css_already_import_stack *imported_stack)
798 {
799   struct css_already_import_stack *cur;
800
801   base->next = base;
802   base->ref  = &base->next;
803   
804   for (cur = imported_stack->next; cur != imported_stack; cur = cur->next) {
805     struct css_already_import_stack *new_stack;
806     new_stack = apr_palloc(pool, sizeof(*new_stack));
807     memset(new_stack, 0, sizeof(*new_stack));
808     new_stack->full_url = apr_pstrdup(pool, cur->full_url);
809     list_insert(new_stack, base);
810   }
811 }
812
813 /* For DEBUG */
814 void
815 chxj_css_stylesheet_dump(css_stylesheet_t *stylesheet)
816 {
817   css_selector_t *cur_sel; 
818   css_property_t *cur_prop;
819
820   for (cur_sel = stylesheet->selector_head.next; cur_sel != &stylesheet->selector_head; cur_sel = cur_sel->next) {
821     fprintf(stderr, "selector:[%s]\n", cur_sel->name);
822     for (cur_prop = cur_sel->property_head.next; cur_prop != &cur_sel->property_head; cur_prop = cur_prop->next) {
823       fprintf(stderr, "\tproperty:\n");
824       fprintf(stderr, "\t\t- name:%s\n", cur_prop->name);
825       fprintf(stderr, "\t\t- value:%s\n", cur_prop->value);
826     }
827   }
828 }
829
830 static void
831 s_get_tag_and_class_and_id(Doc *doc, Node *node, char **tag_name, char **class_name, char **id)
832 {
833   Attr *attr;
834   *tag_name = node->name;
835   for (attr = qs_get_attr(doc, node); attr; attr = qs_get_next_attr(doc,attr)) {
836     char *name  = qs_get_attr_name(doc,attr);
837     char *value = qs_get_attr_value(doc,attr);
838     if (STRCASEEQ('c','C', "class", name)) {
839       if (*value != 0) {
840         *class_name = value;
841       }
842     }
843     else 
844     if (STRCASEEQ('i','I', "id", name)) {
845       if (*value != 0) {
846         *id = value;
847       }
848     }
849     if (*id && *class_name) break;
850   }
851 }
852
853 static char *
854 s_cmp_now_node_vs_current_style(Doc *doc, request_rec *r, apr_pool_t *pool, char *src, ap_regex_t *pattern4, Node *node)
855 {
856   ap_regmatch_t match[256];
857   if (chxj_ap_regexec(pattern4, src, pattern4->re_nsub + 1, match, 0) == 0) {
858     char *tag_name   = chxj_ap_pregsub(pool, "$1", src, pattern4->re_nsub + 1, match);
859     char *class_name = chxj_ap_pregsub(pool, "$2", src, pattern4->re_nsub + 1, match);
860     char *id_name    = chxj_ap_pregsub(pool, "$3", src, pattern4->re_nsub + 1, match);
861     DBG(r, "REQ[%X] tag:[%s] class:[%s] id:[%s]", TO_ADDR(r),tag_name, class_name, id_name);
862     if (!node) {
863       DBG(r, "REQ[%X] unmatch(parent is null)",TO_ADDR(r));
864       return NULL;
865     }
866     char *node_tag_name   = NULL;
867     char *node_class_name = NULL;
868     char *node_id_name    = NULL;
869     s_get_tag_and_class_and_id(doc, node, &node_tag_name, &node_class_name, &node_id_name);
870     if (*tag_name == 0 || strcasecmp(node_tag_name, tag_name) == 0 || strcmp("*", tag_name) == 0) {
871       if (class_name && *class_name != 0) {
872         if (!node_class_name) {
873           DBG(r, "REQ[%X] unmatch (class) node:[NULL]",TO_ADDR(r));
874           return NULL;
875         }
876         if (strcasecmp(node_class_name, &class_name[1]) != 0) {
877           DBG(r,"REQ[%X] unmatch (class) node:[%s] style:[%s]", TO_ADDR(r),node_class_name, &class_name[1]);
878           return NULL;
879         }
880       }
881       if (id_name && *id_name != 0) {
882         if (!node_id_name) {
883           DBG(r,"REQ[%X] unmatch (id) node:[NULL]",TO_ADDR(r));
884           return NULL;
885         }
886         if (strcasecmp(node_id_name, &id_name[1]) != 0) {
887           DBG(r,"REQ[%X] unmatch (id)",TO_ADDR(r));
888           return NULL;
889         }
890       }
891       DBG(r,"REQ[%X] match",TO_ADDR(r));
892       return src;
893     }
894     DBG(r,"REQ[%X] unmatch(tag) tag:[%s] vs [%s]", TO_ADDR(r),tag_name, node_tag_name);
895   }
896   return NULL;
897 }
898
899
900 /**
901  * find selector with pseudo class.
902  * @return new css_stylesheet_t
903  */
904 css_stylesheet_t *
905 chxj_find_pseudo_selectors(Doc *doc, css_stylesheet_t *stylesheet)
906 {
907   css_selector_t *cur_sel; 
908   css_stylesheet_t *result;
909   char *pattern_str = "^a:(link|focus|visited)$";
910   ap_regex_t *pattern1 = chxj_ap_pregcomp(doc->pool, pattern_str, AP_REG_EXTENDED|AP_REG_ICASE);
911
912   result = apr_palloc(doc->pool, sizeof(*result));
913   if (! result) {
914     ERR(doc->r,"REQ[%X] %s:%d Out of Memory", TO_ADDR(doc->r),APLOG_MARK);
915     return NULL;
916   }
917   memset(result, 0, sizeof(*result));
918   result->selector_head.next = &result->selector_head;
919   result->selector_head.ref  = &result->selector_head.next;
920
921   for (cur_sel = stylesheet->selector_head.next; cur_sel != &stylesheet->selector_head; cur_sel = cur_sel->next) {
922     ap_regmatch_t match[256];
923     if (chxj_ap_regexec(pattern1, cur_sel->name, pattern1->re_nsub + 1, match, 0) == 0) {
924       css_selector_t *new_sel = s_new_selector(doc->pool, result, cur_sel->name);
925       css_property_t *cur_prop;
926       for (cur_prop = cur_sel->property_head.next; cur_prop != &cur_sel->property_head; cur_prop = cur_prop->next) {
927         css_property_t *to_prop = s_css_parser_copy_property(doc->pool, cur_prop);
928         list_insert(to_prop, (&new_sel->property_head));
929       }
930       list_insert(new_sel, (&result->selector_head));
931     }
932   }
933
934   chxj_ap_pregfree(doc->pool, pattern1);
935
936   return result;
937 }
938
939 /**
940  * style attribute parser.
941  * @return merged new css_stylesheet_t object.
942  */
943 css_stylesheet_t *
944 chxj_css_parse_style_attr(Doc *doc, css_stylesheet_t *old_stylesheet, char *tag_name, char *class_name, char *id_name, char *style_attr_value)
945 {
946   css_stylesheet_t *new_stylesheet;
947   css_stylesheet_t *dup_stylesheet = NULL;
948   char *attr_value;
949   char *class_name_sel = NULL;
950   char *id_name_sel    = NULL;
951
952   DBG(doc->r,"REQ[%X] start %s()",TO_ADDR(doc->r),__func__);
953
954   if (class_name) {
955     class_name_sel = apr_psprintf(doc->pool, ".%s", class_name);
956   }
957   if (id_name) {
958     id_name_sel = apr_psprintf(doc->pool, "#%s", id_name);
959   }
960
961   attr_value = apr_psprintf(doc->pool, 
962                             "%s%s%s { %s; }",
963                             tag_name, 
964                             (class_name) ?  class_name_sel : "",
965                             (id_name)    ?  id_name_sel    : "",
966                             style_attr_value);
967
968   if (old_stylesheet) {
969     dup_stylesheet = s_dup_stylesheet(doc, old_stylesheet);
970   }
971   new_stylesheet = s_chxj_css_parse_from_buf(doc->r, doc->pool, NULL, dup_stylesheet, attr_value);
972
973   DBG(doc->r,"REQ[%X] end %s()",TO_ADDR(doc->r),__func__);
974   return new_stylesheet;
975 }
976
977
978 static css_stylesheet_t *
979 s_dup_stylesheet(Doc *doc, css_stylesheet_t *stylesheet)
980 {
981   css_selector_t   *cur_sel; 
982   css_stylesheet_t *result;
983
984   result = apr_palloc(doc->pool, sizeof(*result));
985   if (! result) {
986     ERR(doc->r, "REQ[%X] %s:%d Out of Memory", TO_ADDR(doc->r),APLOG_MARK);
987     return NULL;
988   }
989   memset(result, 0, sizeof(*result));
990   result->selector_head.next = &result->selector_head;
991   result->selector_head.ref  = &result->selector_head.next;
992
993   for (cur_sel = stylesheet->selector_head.next; cur_sel != &stylesheet->selector_head; cur_sel = cur_sel->next) {
994     css_selector_t *new_sel = s_new_selector(doc->pool, result, cur_sel->name);
995     css_property_t *cur_prop;
996     for (cur_prop = cur_sel->property_head.next; cur_prop != &cur_sel->property_head; cur_prop = cur_prop->next) {
997       css_property_t *to_prop = s_css_parser_copy_property(doc->pool, cur_prop);
998       list_insert(to_prop, (&new_sel->property_head));
999     }
1000     list_insert(new_sel, (&result->selector_head));
1001   }
1002
1003   return result;
1004 }
1005
1006 /**
1007  * style value parser.
1008  * <style>css</style>
1009  * @return merged new css_stylesheet_t object.
1010  */
1011 css_stylesheet_t *
1012 chxj_css_parse_style_value(Doc *doc, css_stylesheet_t *old_stylesheet, char *style_attr_value)
1013 {
1014   css_stylesheet_t *new_stylesheet;
1015   css_stylesheet_t *dup_stylesheet = NULL;
1016
1017   DBG(doc->r,"REQ[%X] start %s()",TO_ADDR(doc->r),__func__);
1018
1019   if (old_stylesheet) {
1020     dup_stylesheet = s_dup_stylesheet(doc, old_stylesheet);
1021   }
1022   new_stylesheet = s_chxj_css_parse_from_buf(doc->r, doc->pool, NULL, dup_stylesheet, style_attr_value);
1023
1024   DBG(doc->r,"REQ[%X] end %s()",TO_ADDR(doc->r),__func__);
1025   return new_stylesheet;
1026 }
1027
1028
1029 /**
1030  * allocate prop_list_stack.
1031  */
1032 css_prop_list_stack_t *
1033 chxj_new_prop_list_stack(Doc *doc)
1034 {
1035   css_prop_list_stack_t *new_stack = apr_palloc(doc->pool, sizeof(css_prop_list_stack_t));
1036   if (! new_stack) {
1037     ERR(doc->r,"REQ[%X] %s:%d Out of memory.",TO_ADDR(doc->r),APLOG_MARK);
1038     return NULL;
1039   }
1040   new_stack->head.next = &new_stack->head;
1041   new_stack->head.ref  = &new_stack->head.next;
1042   return new_stack;
1043 }
1044
1045 css_prop_list_t *
1046 chxj_css_create_prop_list(Doc *doc, css_selector_t *sel)
1047 {
1048   css_prop_list_t *prop_list;
1049   prop_list = apr_palloc(doc->pool, sizeof(*prop_list));
1050   if (! prop_list) {
1051     ERR(doc->r,"REQ[%X] %s:%d Out of memory.",TO_ADDR(doc->r),APLOG_MARK);
1052     return NULL;
1053   }
1054   prop_list->head.next = &prop_list->head;
1055   prop_list->head.ref  = &prop_list->head.next;
1056   if (sel)  {
1057     css_property_t *cur_prop;
1058     for (cur_prop = sel->property_head.next; 
1059          cur_prop != &sel->property_head; 
1060          cur_prop = cur_prop->next) {
1061       css_property_t *cp_prop = s_css_parser_copy_property(doc->pool, cur_prop);
1062       list_insert(cp_prop, (&prop_list->head));
1063     }
1064   }
1065   return prop_list;
1066 }
1067
1068 /**
1069  * PUSH
1070  */
1071 void
1072 chxj_css_push_prop_list(css_prop_list_stack_t *stack, css_prop_list_t *prop_list)
1073 {
1074   list_insert(prop_list, (&stack->head));
1075 }
1076
1077 /**
1078  * POP
1079  */
1080 css_prop_list_t *
1081 chxj_css_pop_prop_list(css_prop_list_stack_t *stack)
1082 {
1083   css_prop_list_t *tail = chxj_css_get_last_prop_list(stack);
1084   if (! tail) {
1085     return NULL;
1086   }
1087
1088   list_remove(tail);
1089   return tail;
1090 }
1091
1092
1093 /**
1094  * POP but delete.
1095  */
1096 css_prop_list_t *
1097 chxj_css_get_last_prop_list(css_prop_list_stack_t *stack)
1098 {
1099   css_prop_list_t *tail = (css_prop_list_t *)((apr_size_t)stack->head.ref - (apr_size_t)APR_OFFSETOF(css_prop_list_t, next));
1100   if (tail == &stack->head) {
1101     return NULL;
1102   }
1103   return tail;
1104 }
1105
1106 css_prop_list_t *
1107 chxj_dup_css_prop_list(Doc *doc, css_prop_list_t *old)
1108 {
1109   css_property_t *cur_prop;
1110   css_prop_list_t *new_prop_list;
1111
1112   new_prop_list = chxj_css_create_prop_list(doc, NULL);
1113   if (! new_prop_list) {
1114     ERR(doc->r,"REQ[%X] %s:%d Out of memory.",TO_ADDR(doc->r),APLOG_MARK);
1115     return NULL;
1116   }
1117   if (old) {
1118     for (cur_prop = old->head.next; cur_prop != &old->head; cur_prop = cur_prop->next) {
1119       css_property_t *cp_prop = s_css_parser_copy_property(doc->pool, cur_prop);
1120       list_insert(cp_prop, (&new_prop_list->head));
1121     }
1122   }
1123   return new_prop_list;
1124 }
1125 void
1126 chxj_css_prop_list_merge_property(Doc *doc, css_prop_list_t *base, css_selector_t *sel)
1127 {
1128   css_property_t *cur_prop;
1129   css_property_t *b_prop;
1130   for (cur_prop = sel->property_head.next; cur_prop != &sel->property_head; cur_prop = cur_prop->next) {
1131     int found = 0;
1132     for (b_prop = base->head.next; b_prop != &base->head; b_prop = b_prop->next) {
1133       if (cur_prop->name && b_prop->name && strcasecmp(cur_prop->name, b_prop->name) == 0) {
1134         found = 1;
1135         b_prop->value = apr_pstrdup(doc->pool, cur_prop->value);
1136       }
1137     }
1138     if (! found) {
1139       css_property_t *cp_prop = s_css_parser_copy_property(doc->pool, cur_prop);
1140       list_insert(cp_prop, (&base->head));
1141     }
1142   }
1143   
1144 }
1145
1146 css_property_t *
1147 chxj_css_get_property_value(Doc *doc, css_prop_list_t *base, const char *name)
1148 {
1149   css_property_t *b_prop;
1150   css_property_t *result;
1151
1152   result = apr_palloc(doc->pool, sizeof(*result));
1153   memset(result, 0, sizeof(*result));
1154   result->next = result;
1155   result->ref  = &result->next;
1156
1157   for (b_prop = base->head.next; b_prop != &base->head; b_prop = b_prop->next) {
1158     if (b_prop->name && strcasecmp(name, b_prop->name) == 0) {
1159       css_property_t *tmp;
1160
1161       tmp = apr_palloc(doc->pool, sizeof(*tmp));
1162       memset(tmp, 0, sizeof(*tmp));
1163       tmp->next = tmp;
1164       tmp->ref  = &tmp->next;
1165
1166       tmp->value = apr_pstrdup(doc->pool, b_prop->value);
1167       list_insert(tmp, result);
1168     }
1169   }
1170   return result;
1171 }
1172
1173
1174 char *
1175 chxj_css_rgb_func_to_value(apr_pool_t *pool, const char *rgb_func_string)
1176 {
1177   char *s = apr_pstrdup(pool, rgb_func_string);
1178   if (chxj_starts_with(s, "rgb")) {
1179     s += 3;
1180     s = qs_trim_string(pool, s);
1181     if (*s == '(') s++;
1182     int len = strlen(s);
1183     if (s[len - 1] == ')') s[len-1] = 0;
1184     char *pstat;
1185     char *red   = qs_trim_string(pool, apr_strtok(s, ",", &pstat));
1186     char *green = qs_trim_string(pool, apr_strtok(NULL, ",", &pstat));
1187     char *blue  = qs_trim_string(pool, apr_strtok(NULL, ",", &pstat));
1188     int red_per_flag   = 0;
1189     int green_per_flag = 0;
1190     int blue_per_flag  = 0;
1191     if (red && (pstat = strchr(red, '%'))) {
1192       *pstat = 0;
1193       red_per_flag = 1;
1194     }
1195     else {
1196       red = "0";
1197     }
1198     if (green && (pstat = strchr(green, '%'))) {
1199       *pstat = 0;
1200       green_per_flag = 1;
1201     }
1202     else {
1203       green = "0";
1204     }
1205     if (blue && (pstat = strchr(blue, '%'))) {
1206       *pstat = 0;
1207       blue_per_flag = 1;
1208     }
1209     else {
1210       blue = "0";
1211     }
1212     double d_red   = (double)chxj_atoi(red);
1213     double d_green = (double)chxj_atoi(green);
1214     double d_blue  = (double)chxj_atoi(blue);
1215     if (red_per_flag) {
1216       d_red   /= 100.0;
1217       d_red   *= 255;
1218     }
1219     if (green_per_flag) {
1220       d_green /= 100.0;
1221       d_green *= 255;
1222     }
1223     if (blue_per_flag) {
1224       d_blue  /= 100.0;
1225       d_blue  *= 255;
1226     }
1227     char *ret = apr_psprintf(pool, "#%02x%02x%02x", (int)d_red, (int)d_green, (int)d_blue);
1228     return ret;
1229   }
1230   else if (s[0] == '#') {
1231     if (strlen(&s[1]) == 3) {
1232       char red[2];
1233       char green[2];
1234       char blue[2];
1235       red[0]   = s[1];
1236       green[0] = s[2];
1237       blue[0]  = s[3];
1238       red[1]   = 0;
1239       green[1] = 0;
1240       blue[1]  = 0;
1241       int ired   = chxj_axtoi(red);
1242       int igreen = chxj_axtoi(green);
1243       int iblue  = chxj_axtoi(blue);
1244       return apr_psprintf(pool, "#%02x%02x%02x", ired * 0x10 + ired, igreen * 0x10 + igreen, iblue * 0x10 + iblue);
1245     }
1246   }
1247   else {
1248     switch(*s) {
1249     case 'a':
1250     case 'A':
1251       if (STRCASEEQ('q','Q', &s[1], "qua")) {
1252         /* aqua */
1253         return apr_pstrdup(pool, "#00ffff");
1254       }
1255       break;
1256     case 'b':
1257     case 'B':
1258       if (s[1] == 'l' || s[1] == 'L') {
1259         if (s[2] == 'a' || s[2] == 'A') {
1260           if (STRCASEEQ('c','C',&s[3], "ck")) {
1261             /* black */
1262             return apr_pstrdup(pool, "#000000");
1263           }
1264         }
1265         else if (s[2] == 'u' || s[2] == 'U') {
1266           if (s[3] == 'e' || s[3] == 'E') {
1267             /* blue */
1268             return apr_pstrdup(pool, "#0000ff");
1269           }
1270         }
1271       }
1272       break;
1273     case 'f':
1274     case 'F':
1275       if (STRCASEEQ('u','U',&s[1], "uchsia")) {
1276         /* fuchsia */
1277         return apr_pstrdup(pool, "#ff00ff");
1278       }
1279       break;
1280     case 'g':
1281     case 'G':
1282       if (s[1] == 'r'||s[1] == 'R') {
1283         if (STRCASEEQ('a','A', &s[2], "ay")) {
1284           /* gray */
1285           return apr_pstrdup(pool, "#808080");
1286         }
1287         else if (STRCASEEQ('e','E',&s[2], "een")) {
1288           /* green */
1289           return apr_pstrdup(pool, "#008000");
1290         }
1291       }
1292       break;
1293     case 'l':
1294     case 'L':
1295       if (STRCASEEQ('i','I', &s[1], "ime")) {
1296         /* lime */
1297         return apr_pstrdup(pool, "#00ff00");
1298       }
1299       break;
1300     case 'm':
1301     case 'M':
1302       if (STRCASEEQ('a','A', &s[1], "aroon")) {
1303         /* maroon */
1304         return apr_pstrdup(pool, "#800000");
1305       }
1306       break;
1307     case 'n':
1308     case 'N':
1309       if (STRCASEEQ('a','A', &s[1], "avy")) {
1310         /* navy */
1311         return apr_pstrdup(pool, "#000080");
1312       }
1313       break;
1314     case 'o':
1315     case 'O':
1316       if (STRCASEEQ('l','L', &s[1], "live")) {
1317         /* olive */
1318         return apr_pstrdup(pool, "#808000");
1319       }
1320       break;
1321     case 'p':
1322     case 'P':
1323       if (STRCASEEQ('u','U', &s[1], "urple")) {
1324         /* purple */
1325         return apr_pstrdup(pool, "#800080");
1326       }
1327       break;
1328     case 'r':
1329     case 'R':
1330       if (STRCASEEQ('e','E', &s[1], "ed")) {
1331         /* red */
1332         return apr_pstrdup(pool, "#ff0000");
1333       }
1334       break;
1335     case 's':
1336     case 'S':
1337       if (STRCASEEQ('i','I', &s[1], "ilver")) {
1338         /* silver */
1339         return apr_pstrdup(pool, "#c0c0c0");
1340       }
1341       break;
1342     case 't':
1343     case 'T':
1344       if (STRCASEEQ('e','E', &s[1], "eal")) {
1345         /* teal */
1346         return apr_pstrdup(pool, "#008080");
1347       }
1348       break;
1349     case 'w':
1350     case 'W':
1351       if (STRCASEEQ('h','H', &s[1], "hite")) {
1352         /* white */
1353         return apr_pstrdup(pool, "#ffffff");
1354       }
1355       break;
1356     case 'y':
1357     case 'Y':
1358       if (STRCASEEQ('y','Y', &s[1], "ellow")) {
1359         /* yellow */
1360         return apr_pstrdup(pool, "#ffff00");
1361       }
1362       break;
1363     default:
1364       break;
1365     }
1366   }
1367   return s;
1368 }
1369
1370 /*
1371  * vim:ts=2 et
1372  */