OSDN Git Service

* temp commit.
authorAtsushi Konno <konn@users.sourceforge.jp>
Wed, 21 May 2008 16:11:44 +0000 (01:11 +0900)
committerAtsushi Konno <konn@users.sourceforge.jp>
Wed, 21 May 2008 16:11:44 +0000 (01:11 +0900)
include/chxj_css.h
include/chxj_encoding.h
include/chxj_serf.h
src/chxj_css.c
src/chxj_encoding.c
src/chxj_serf.c
test/chxj_css/Makefile [new file with mode: 0644]
test/chxj_css/test_chxj_css.c [new file with mode: 0644]

index 891c7b9..2f4bd6c 100644 (file)
@@ -39,8 +39,7 @@ typedef struct __css_selector_t {
   struct __css_selector_t **ref;
   /* has tag or/and class or/and id */
   char *name;
-  css_property_t *head;
-  css_property_t *tail;
+  css_property_t property_head;
 } css_selector_t;
 
 
@@ -49,8 +48,7 @@ typedef struct __css_selector_t {
  * Manager of css_selector_t.
  */
 typedef struct __css_stylesheet_t {
-  css_selector_t *head;
-  css_selector_t *tail;
+  css_selector_t selector_head;
 } css_stylesheet_t;
 
 
@@ -58,8 +56,7 @@ typedef struct __css_stylesheet_t {
  * CSS current_stylesheet.
  */
 typedef struct __css_current_stylesheet_t {
-  struct __css_property_t *head;
-  struct __css_property_t *tail;
+  struct __css_property_t property_head;
   struct __css_current_stylesheet_t *next;
   struct __css_current_stylesheet_t **ref;
 } css_current_stylesheet_t;
@@ -69,8 +66,8 @@ typedef struct __css_current_stylesheet_t {
  * CSS current_stylesheet_stack_t.
  */
 typedef struct __css_current_stylesheet_stack_t {
-  css_current_stylesheet_t *head;
-  css_current_stylesheet_t *tail;
+  css_current_stylesheet_t *stylesheet_head;
+  css_current_stylesheet_t *stylesheet_tail;
 } css_current_stylesheet_stack_t;
 
 #endif
index 29ecf83..2565385 100644 (file)
 /*----------------------------------------------------------------------------*/
 /* Prototype                                                                  */
 /*----------------------------------------------------------------------------*/
-extern charchxj_encoding(
-  request_rec*       r, 
-  const char*        src, 
-  apr_size_t*        len);
+extern char *chxj_encoding(
+  request_rec *r, 
+  const char  *src, 
+  apr_size_t  *len);
 
-extern charchxj_rencoding(
-  request_rec*       r, 
-  const char*        src, 
-  apr_size_t*        len);
+extern char *chxj_rencoding(
+  request_rec *r, 
+  const char *src, 
+  apr_size_t *len);
 
-extern charchxj_encoding_parameter(
-  request_rec*       r, 
-  const char*        value);
+extern char *chxj_encoding_parameter(
+  request_rec *r, 
+  const char  *value);
 
+
+char *chxj_iconv(request_rec *r, apr_pool_t *pool, const char *src, apr_size_t *len, const char *from, const char *to);
 #endif
 
index a5b9092..a542998 100644 (file)
@@ -32,7 +32,7 @@
  */
 #include "serf.h"
 
-extern char *chxj_serf_get(request_rec *r, apr_pool_t *ppool, const char *url_path);
+extern char * (*chxj_serf_get)(request_rec *r, apr_pool_t *ppool, const char *url_path);
 
 #endif
 /*
index a3af1e1..10e3f12 100644 (file)
  * limitations under the License.
  */
 #include "chxj_css.h"
+#include "chxj_serf.h"
+#include "chxj_encoding.h"
+#include "qs_parse_string.h"
 #include "apr_pools.h"
 
+
 /*===========================================================================*/
 /* PARSER                                                                    */
 /*===========================================================================*/
+static char *s_css_parser_get_charset(apr_pool_t *pool, const char *src, apr_size_t *next_pos);
 static void s_css_parser_from_uri_start_selector(CRDocHandler * a_this, CRSelector *a_selector_list);
 static void s_css_parser_from_uri_end_selector(CRDocHandler * a_this, CRSelector *a_selector_list);
 static void s_css_parser_from_uri_property(CRDocHandler *a_this, CRString *a_name, CRTerm *a_expression, gboolean a_is_important);
+static css_property_t *s_css_parser_copy_property(apr_pool_t *pool, css_property_t *from);
+static css_selector_t *s_new_selector(apr_pool_t *pool, css_stylesheet_t *stylesheet, char *name);
+static css_selector_t *s_search_selector(css_stylesheet_t *stylesheet, const char *name);
+static void s_merge_property(css_selector_t *sel, css_property_t *tgt);
+
+struct css_app_data {
+  css_stylesheet_t *stylesheet;
+  char **selector_list;
+  int selector_count;
+  apr_pool_t *pool;
+  request_rec *r;
+  int error_occured;
+  css_property_t property_head;
+};
+
 
 css_stylesheet_t *
-chxj_css_parse_from_uri(apr_pool_t *pool, css_stylesheet_t *old_stylesheet, const char *uri)
+chxj_css_parse_from_uri(request_rec *r, apr_pool_t *pool, css_stylesheet_t *old_stylesheet, const char *uri)
 {
-  CRParser     *parser      = NULL ;
-  CRDocHandler *sac_handler = NULL ;
+  CRParser     *parser      = NULL;
+  CRDocHandler *sac_handler = NULL;
   enum CRStatus ret;
+  char         *css         = NULL;
+  char         *charset     = NULL;
+  apr_size_t  srclen;
+  apr_size_t  next_pos;
+  css_stylesheet_t *stylesheet = NULL;
+  struct css_app_data app_data;
+  
 
-  parser = cr_parser_new_from_file(uri, CR_AUTO);
-  if (!parser) {
-    fprintf(stderr, "parserオブジェクト失敗\n");
+  DBG(r, "start chxj_css_parse_from_uri()");
+
+  css = chxj_serf_get(r, pool, uri);
+  if (css == NULL) {
+    ERR(r, "%s:%d end chxj_css_parse_from_uri(): serf_get failed: url:[%s]", APLOG_MARK, uri);
     return NULL;
   }
+  srclen = strlen(css);
+  
 
+  charset = s_css_parser_get_charset(pool, css, &next_pos);
+  if (charset) {
+    DBG(r, "charset:[%s]\n", charset);
+    css += next_pos;
+    srclen = strlen(css);
+    css = chxj_iconv(r, pool, css, &srclen, charset, "UTF-8");
+  }
+
+  parser = cr_parser_new_from_buf((guchar *)css, srclen, CR_UTF_8, FALSE);
+  if (!parser) {
+    ERR(r, "%s:%d end chxj_css_parse_from_uri(): cr_parser_new_from_buf() failed", APLOG_MARK);
+    return NULL;
+  }
   sac_handler = cr_doc_handler_new();
   if (!sac_handler) {
-    fprintf(stderr, "sac_handlerオブジェクト失敗\n");
+    ERR(r, "%s:%d end chxj_css_parse_from_uri(): cr_doc_handler_new() failed", APLOG_MARK);
     cr_parser_destroy(parser);
     return NULL;
   }
 
-  sac_handler->app_data = "こんちは";
+  stylesheet = apr_palloc(pool, sizeof(*stylesheet));
+  memset(stylesheet, 0, sizeof(*stylesheet));
+  stylesheet->selector_head.next = &stylesheet->selector_head;
+  stylesheet->selector_head.ref  = &stylesheet->selector_head.next;
+
+  memset(&app_data, 0, sizeof(struct css_app_data));
+  app_data.stylesheet = stylesheet;
+  app_data.selector_list = NULL;
+  app_data.selector_count = 0;
+  app_data.pool = pool;
+  app_data.error_occured = 0;
+  app_data.r = r;
+
+  sac_handler->app_data = &app_data;
 
   sac_handler->start_selector = s_css_parser_from_uri_start_selector;
   sac_handler->end_selector   = s_css_parser_from_uri_end_selector;
@@ -52,71 +109,248 @@ chxj_css_parse_from_uri(apr_pool_t *pool, css_stylesheet_t *old_stylesheet, cons
 
   ret = cr_parser_set_sac_handler(parser, sac_handler);
   if (ret != CR_OK) {
-    fprintf(stderr, "なんかエラーが起きた:[%d]\n", ret);
+    ERR(r, "%s:%d end chxj_css_parse_from_uri(): cr_parser_set_sac_handler() failed: ret:[%d]", APLOG_MARK, ret);
     cr_parser_destroy(parser);
     return NULL;
   }
 
   ret = cr_parser_parse(parser);
-  return NULL;
+  cr_parser_destroy(parser);
+  DBG(r, "end chxj_css_parse_from_uri()");
+  return app_data.stylesheet;
 }
 
 
+#define ERROR_OCCORED do {          \
+    if (app_data->error_occured) {  \
+      return;                       \
+    }                               \
+  }                                 \
+  while (0)
+
+
+#define CB_INIT \
+  struct css_app_data *app_data = (struct css_app_data *)a_this->app_data
+
+#undef list_insert
+#undef list_remove
+#define list_insert(node, point) do {           \
+    node->ref = point->ref;                     \
+    *node->ref = node;                          \
+    node->next = point;                         \
+    point->ref = &node->next;                   \
+} while (0)
+
+#define list_remove(node) do {                  \
+    *node->ref = node->next;                    \
+    node->next->ref = node->ref;                \
+} while (0)
+
+
 static void 
 s_css_parser_from_uri_start_selector(CRDocHandler * a_this, CRSelector *a_selector_list)
 {
-  if (a_selector_list) {
-    CRSelector *cur = NULL;
-    for (cur = a_selector_list; cur; cur = cur->next) {
-      if (cur->simple_sel) {
-        guchar *tmp_str = cr_simple_sel_to_string(cur->simple_sel);
-        if (tmp_str) {
-          printf("start selector:[%s]\n", tmp_str);
-          g_free (tmp_str);
-          tmp_str = NULL;
-        }
+  int ii;
+  CRSelector *cur = NULL;
+  CB_INIT;
+  ERROR_OCCORED;
+
+  app_data->selector_count = 0;
+  for (cur = a_selector_list; cur; cur = cur->next)
+    app_data->selector_count++;
+
+  app_data->selector_list = apr_palloc(app_data->pool, sizeof(char *) * app_data->selector_count);
+  if (! app_data->selector_list) {
+    ERR(app_data->r, "%s:%d Out of memory", APLOG_MARK);
+    app_data->error_occured = 1;
+    return;
+  }
+
+  ii = 0;
+  for (cur = a_selector_list; cur; cur = cur->next) {
+    if (cur->simple_sel) {
+      guchar *tmp_str = cr_simple_sel_to_string(cur->simple_sel);
+      if (tmp_str) {
+        app_data->selector_list[ii++] = apr_pstrdup(app_data->pool, (char *)tmp_str);
+        g_free (tmp_str);
+        tmp_str = NULL;
       }
     }
   }
+  app_data->property_head.next = &app_data->property_head;
+  app_data->property_head.ref  = &app_data->property_head.next;
 }
 
+
 static void 
 s_css_parser_from_uri_end_selector(CRDocHandler * a_this, CRSelector *a_selector_list)
 {
-  printf("%s\n", a_this->app_data);
-  if (a_selector_list) {
-    CRSelector *cur = NULL;
-    for (cur = a_selector_list; cur; cur = cur->next) {
-      if (cur->simple_sel) {
-        guchar *tmp_str = cr_simple_sel_to_string(cur->simple_sel);
-        if (tmp_str) {
-          printf("end selector:[%s]\n", tmp_str);
-          g_free (tmp_str);
-          tmp_str = NULL;
-        }
+  int ii;
+  css_property_t *cur = NULL;
+  CB_INIT;
+  ERROR_OCCORED;
+
+  if (app_data->property_head.next)  {
+    for (ii=0; ii<app_data->selector_count; ii++) {
+      css_selector_t *sel = s_new_selector(app_data->pool, app_data->stylesheet, app_data->selector_list[ii]); 
+
+      for (cur = app_data->property_head.next; cur && cur != &app_data->property_head; cur = cur->next) {
+        css_property_t *tgt = s_css_parser_copy_property(app_data->pool, cur);
+        css_property_t *pnt = &sel->property_head;
+        s_merge_property(sel, tgt);
       }
+      css_selector_t *point_selector = &app_data->stylesheet->selector_head;
+      list_insert(sel, point_selector);
     }
   }
+  app_data->property_head.next = &app_data->property_head;
+  app_data->property_head.ref  = &app_data->property_head.next;
 }
 
 static void
+s_merge_property(css_selector_t *sel, css_property_t *tgt)
+{
+  css_property_t *cur;
+  css_property_t *pnt = &sel->property_head;
+  char l = tolower(*tgt->name);
+  char u = toupper(*tgt->name);
+  for (cur = pnt->next; cur != pnt;cur = cur->next) {
+    if ((l == *cur->name || u == *cur->name) && strcasecmp(cur->name, tgt->name) == 0) {
+      cur->value = tgt->value;
+      return;
+    }
+  }
+  list_insert(tgt,  pnt);
+}
+
+static css_selector_t *
+s_new_selector(apr_pool_t *pool, css_stylesheet_t *stylesheet, char *name)
+{
+  css_selector_t *sel = NULL;
+  sel = s_search_selector(stylesheet, name);
+  if (sel) {
+    list_remove(sel);
+    sel->next = sel;
+    sel->ref = &sel->next;
+  }
+  else {
+    sel = apr_palloc(pool, sizeof(css_selector_t));
+    memset(sel, 0, sizeof(css_selector_t));
+    sel->name = name;
+    sel->next = sel;
+    sel->ref = &sel->next;
+    sel->property_head.next = &sel->property_head;
+    sel->property_head.ref = &sel->property_head.next;
+  }
+  return sel;
+}
+
+static css_selector_t *
+s_search_selector(css_stylesheet_t *stylesheet, const char *name)
+{
+  css_selector_t *cur;
+  char l = tolower(*name);
+  char u = toupper(*name);
+  for (cur = stylesheet->selector_head.next; cur != &stylesheet->selector_head; cur = cur->next) {
+    if ((l == *cur->name || u == *cur->name) && strcasecmp(cur->name, name) == 0) {
+      return cur;
+    }
+  }
+  return NULL;
+}
+
+static css_property_t *
+s_css_parser_copy_property(apr_pool_t *pool, css_property_t *from)
+{
+  css_property_t *prop = apr_palloc(pool, sizeof(css_property_t));
+  prop->name  = apr_pstrdup(pool, from->name);
+  prop->value = apr_pstrdup(pool, from->value);
+  prop->next  = prop;
+  prop->ref   = &prop->next;
+  return prop;
+}
+
+
+
+
+static void
 s_css_parser_from_uri_property(CRDocHandler *a_this, CRString *a_name, CRTerm *a_expression, gboolean a_is_important)
 {
-  printf("%s\n", a_this->app_data);
-  if (a_name) {
-    printf("property: name:[%s]\n", cr_string_peek_raw_str(a_name));
-  }  
-  if (a_expression) {
-    CRTerm *cur = NULL;
-    for (cur = a_expression; cur; cur = cur->next) {
-      guchar *tmp = cr_term_one_to_string(cur);
-      printf("property: value:[%s]\n", tmp);
-      g_free(tmp);
-      tmp = NULL;
+  CB_INIT;
+  ERROR_OCCORED;
+  css_property_t *property;
+
+  if (a_name && a_expression) {
+    guchar *tmp_str;
+    property = apr_palloc(app_data->pool, sizeof(*property));
+    memset(property, 0, sizeof(*property));
+    property->name = apr_pstrdup(app_data->pool, cr_string_peek_raw_str(a_name));
+    tmp_str = cr_term_one_to_string(a_expression);
+    property->value = apr_pstrdup(app_data->pool, (char *)tmp_str);
+    g_free(tmp_str);
+    tmp_str = NULL;
+
+    css_property_t *point_property = &app_data->property_head;
+    list_insert(property, point_property);
+  }
+}
+
+
+static char *
+s_css_parser_get_charset(apr_pool_t *pool, const char *src, apr_size_t *next_pos)
+{
+  register char *p = (char *)src;
+  char *sv;
+  char *ret = NULL;
+   
+  if (! p) {
+    return NULL;
+  }
+
+  for (; *p && is_white_space(*p); p++)
+    ;
+
+#define CUT_TOKEN(X) \
+        do { \
+          sv = ++p; \
+          for (;*p && (X); p++) \
+            ; \
+          ret = apr_palloc(pool, p - sv + 1); \
+          memset(ret, 0, p - sv + 1); \
+          memcpy(ret, sv, p - sv);  \
+        } \
+        while (0)
+
+  if (*p == '@') {
+    if (strncasecmp(p, "@charset", sizeof("@charset")-1) == 0) {
+      p += sizeof("@charset");
+      for (; *p && is_white_space(*p); p++)
+        ;
+      if (*p == '"') {
+        CUT_TOKEN(*p != '"');
+        if (! *p) return NULL;
+      }
+      else if (*p == '\'') {
+        CUT_TOKEN(*p != '\'');
+        if (! *p) return NULL;
+      }
+      else {
+        CUT_TOKEN(! is_white_space(*p));
+        if (! *p) return NULL;
+      }
     }
   }
+  if (ret) {
+    *next_pos = p - src + 1;
+  }
+  else {
+    *next_pos = 0;
+  }
+#undef CUT_TOKEN
+  return ret;
 }
 
+
 #if 0
 css_stylesheet_t *
 chxj_css_parse_from_style_tag(apr_pool_t *pool, css_stylesheet_t *old_stylesheet, const char *style_value)
index bf67460..f807016 100644 (file)
@@ -303,6 +303,74 @@ chxj_encoding_parameter(request_rec *r, const char *value)
 
   return apr_pstrcat(r->pool, src_sv, "?", param, NULL);
 }
+
+
+char *
+chxj_iconv(request_rec *r, apr_pool_t *pool, const char *src, apr_size_t *len, const char *from, const char *to)
+{
+  char                *obuf;
+  char                *ibuf;
+  char                *spos;
+  
+  iconv_t             cd;
+  size_t              result;
+  apr_size_t          ilen;
+  apr_size_t          olen;
+  mod_chxj_config     *dconf;
+  chxjconvrule_entry  *entryp;
+
+
+  if ((int)*len < 0) {
+    ERR(r, "runtime exception: chxj_iconv(): invalid string size.[%d]", (int)*len);
+    return (char *)apr_pstrdup(pool, "");
+  }
+
+  ilen = *len;
+  ibuf = apr_palloc(pool, ilen+1);
+  if (ibuf == NULL) {
+    ERR(r, "runtime exception: chxj_iconv(): Out of memory.");
+    return (char *)src;
+  }
+  memset(ibuf, 0, ilen+1);
+  memcpy(ibuf, src, ilen);
+
+  olen = ilen * 4 + 1;
+  spos = obuf = apr_palloc(pool, olen);
+  if (obuf == NULL) {
+    ERR(r, "%s:%d runtime exception: chxj_iconv(): Out of memory", APLOG_MARK);
+    return ibuf;
+  }
+  memset(obuf, 0, olen);
+  cd = iconv_open(to, from);
+  if (cd == (iconv_t)-1) {
+    if (EINVAL == errno) {
+      ERR(r, "The conversion from %s to %s is not supported by the implementation.", from, to);
+    }
+    else {
+      ERR(r, "iconv open failed. from:[%s] to:[%s] errno:[%d]", from, to, errno);
+    }
+    return ibuf;
+  }
+  while (ilen > 0) {
+    result = iconv(cd, &ibuf, &ilen, &obuf, &olen);
+    if (result == (size_t)(-1)) {
+      if (E2BIG == errno) {
+        ERR(r, "There is not sufficient room at *outbuf.");
+      }
+      else if (EILSEQ == errno) {
+        ERR(r, "An invalid multibyte sequence has been encountered in the input. input:[%s]", ibuf);
+      }
+      else if (EINVAL == errno) {
+        ERR(r, "An incomplete multibyte sequence has been encountered in the input. input:[%s]", ibuf);
+      }
+      break;
+    }
+  }
+  *len = strlen(spos);
+  iconv_close(cd);
+
+  return spos;
+}
 /*
  * vim:ts=2 et
  */
index 128c3b4..844b577 100644 (file)
@@ -48,6 +48,10 @@ struct __handler_ctx_t {
   char *response;
 };
 
+static char *s_chxj_serf_get(request_rec *r, apr_pool_t *ppool, const char *url_path);
+
+char *(*chxj_serf_get)(request_rec *r, apr_pool_t *ppool, const char *url_path) = s_chxj_serf_get;
+
 
 void
 s_init(apr_pool_t *ppool, apr_pool_t **pool)
@@ -195,8 +199,8 @@ s_setup_request(serf_request_t           *request,
     return APR_SUCCESS;
 }
 
-char *
-chxj_serf_get(request_rec *r, apr_pool_t *ppool, const char *url_path)
+static char *
+s_chxj_serf_get(request_rec *r, apr_pool_t *ppool, const char *url_path)
 {
   apr_pool_t *pool;
   apr_uri_t url;
diff --git a/test/chxj_css/Makefile b/test/chxj_css/Makefile
new file mode 100644 (file)
index 0000000..175bb86
--- /dev/null
@@ -0,0 +1,26 @@
+.SUFFIXES:
+.SUFFIXES: .c.o
+
+TARGET= test_chxj_css
+
+TOP_DIR=../..
+
+INC_DIR=-I$(TOP_DIR)/include \
+  -I/usr/include/apache2 \
+  -I/usr/include/apr-1.0 \
+  -I$(TOP_DIR)/src \
+  -I$(TOP_DIR)/src/serf
+  
+
+all:test
+
+
+
+test: $(TARGET)
+       ./$(TARGET)
+
+$(TARGET): $(TARGET).c
+       gcc -g -o $@ -Wall -lcunit $< $(INC_DIR) -DLINUX=2 -D_REENTRANT -D_GNU_SOURCE -D_LARGEFILE64_SOURCE  -lapr-1 -laprutil-1 `croco-0.6-config --cflags`  `croco-0.6-config --libs` $(TOP_DIR)/src/serf/.libs/libserf-0.a
+
+clean:
+       rm -f ./$(TARGET)
diff --git a/test/chxj_css/test_chxj_css.c b/test/chxj_css/test_chxj_css.c
new file mode 100644 (file)
index 0000000..1e84c5f
--- /dev/null
@@ -0,0 +1,208 @@
+#include <CUnit/CUnit.h>
+#include <CUnit/Console.h>
+#include <CUnit/Basic.h>
+#include <stdio.h>
+#define CHXJ_TEST
+#define IMG_NOT_CONVERT_FILENAME
+
+#include "mod_chxj.h"
+#include "chxj_apache.h"
+#include "apr.h"
+#include "apr_strings.h"
+#include "chxj_serf.h"
+#include "chxj_css.h"
+#include "chxj_serf.c"
+#include "chxj_css.c"
+#include "chxj_encoding.c"
+#include "chxj_url_encode.c"
+#include "chxj_apache.c"
+
+
+
+void test_chxj_css_parse_from_uri_001();
+void test_chxj_css_parse_from_uri_002();
+/* pend */
+
+int
+main()
+{
+  CU_pSuite css_suite;
+  CU_initialize_registry();
+  css_suite = CU_add_suite("test chxj_css", NULL, NULL);
+
+  CU_add_test(css_suite, "test css 001",                                    test_chxj_css_parse_from_uri_001);
+  CU_add_test(css_suite, "test css 002",                                    test_chxj_css_parse_from_uri_002);
+  /* aend */
+
+  CU_basic_run_tests();
+  CU_cleanup_registry();
+
+  return(0);
+}
+
+void test_set_content_type(request_rec *r, const char *ct)
+{
+  fprintf(stderr, "%s:%d set_content_type(%s)\n", __FILE__,__LINE__,ct);
+}
+
+char *test_ap_escape_html(apr_pool_t *pool, const char *s)
+{
+  return (char *)s;
+}
+
+char *test_os_escape_path(apr_pool_t *p, const char *path, int partial)
+{
+  return (char *)path;
+}
+
+const char *test_run_http_scheme(request_rec *r)
+{
+  static char *s = "http";
+  return s;
+}
+
+void * test_get_module_config(const ap_conf_vector_t *cv, const module *m)
+{
+  static mod_chxj_config cfg;
+  memset(&cfg, 0, sizeof(mod_chxj_config));
+  cfg.new_line_type = NLTYPE_NONE;
+  return &cfg;
+}
+
+chxjconvrule_entry *
+chxj_apply_convrule(request_rec* r, apr_array_header_t* convrules)
+{
+  static chxjconvrule_entry entries;
+  memset(&entries, 0, sizeof(chxjconvrule_entry));
+  entries.encoding = apr_pstrdup(r->pool, "UTF8");
+  return &entries;
+}
+
+void test_log_rerror(const char *file, int line, int level, apr_status_t status, const request_rec *r, const char *fmt, ...)
+{
+  va_list ap;
+  fprintf(stderr, "ERROR LOG %s:%d ", file,line);
+  va_start(ap, fmt);
+  vfprintf(stderr, fmt,ap);
+  va_end(ap);
+  fprintf(stderr, "\n");
+}
+void test_log_error(const char *file, int line, int level, apr_status_t status, const request_rec *r, const char *fmt, ...)
+{
+  va_list ap;
+  fprintf(stderr, "ERROR LOG %s:%d ", file,line);
+  va_start(ap, fmt);
+  vfprintf(stderr, fmt,ap);
+  va_end(ap);
+  fprintf(stderr, "\n");
+}
+
+#define APR_INIT \
+  request_rec r; \
+  apr_pool_t *p; \
+  do { \
+    apr_initialize(); \
+    apr_pool_create(&p, NULL); \
+    r.pool = p; \
+    r.hostname = apr_pstrdup(p, "localhost"); \
+  } \
+  while (0)
+
+#define APR_TERM \
+  do { \
+    apr_terminate(); \
+  } while (0)
+
+
+/** TEST CASE START */
+char *test_chxj_serf_get001(request_rec *r, apr_pool_t *ppool, const char *uri_path)
+{
+  static char *css = "html { display: none }";
+
+  return css;
+}
+void test_chxj_css_parse_from_uri_001()
+{
+  css_stylesheet_t *ret;
+  APR_INIT;
+  chxj_serf_get = test_chxj_serf_get001;
+
+  ret = chxj_css_parse_from_uri(&r, r.pool, NULL, "http://localhost/hoge.css");
+  CU_ASSERT(ret != NULL);
+  {
+    css_selector_t *cur;
+    int ii = 0;
+    int jj = 0;
+    for (cur = ret->selector_head.next; cur != &ret->selector_head; cur = cur->next) {
+      css_property_t *cur_prop;
+      switch(ii) {
+      case 0: CU_ASSERT(strcmp(cur->name, "html") == 0); break;
+      }
+      jj = 0;
+      for (cur_prop = cur->property_head.next; cur_prop != &cur->property_head; cur_prop = cur_prop->next) {
+        switch (jj) {
+        case 0: 
+          CU_ASSERT(strcmp(cur_prop->name, "display") == 0);
+          CU_ASSERT(strcmp(cur_prop->value, "none") == 0);
+          break;
+        }
+        jj++;
+      }
+      ii++;
+    }
+    CU_ASSERT(ii == 1);
+    CU_ASSERT(jj == 1);
+  }
+
+
+  APR_TERM;
+}
+
+
+
+char *test_chxj_serf_get002(request_rec *r, apr_pool_t *ppool, const char *uri_path)
+{
+  static char *css = "html,body { display: none }\nhtml,body { display: none }";
+
+  return css;
+}
+void test_chxj_css_parse_from_uri_002()
+{
+  css_stylesheet_t *ret;
+  APR_INIT;
+  chxj_serf_get = test_chxj_serf_get002;
+
+  ret = chxj_css_parse_from_uri(&r, r.pool, NULL, "http://localhost/hoge.css");
+  CU_ASSERT(ret != NULL);
+  {
+    css_selector_t *cur;
+    int ii = 0;
+    int jj = 0;
+    for (cur = ret->selector_head.next; cur != &ret->selector_head; cur = cur->next) {
+      css_property_t *cur_prop;
+      switch(ii) {
+      case 0: CU_ASSERT(strcmp(cur->name, "html") == 0); break;
+      case 1: CU_ASSERT(strcmp(cur->name, "body") == 0); break;
+      }
+      jj = 0;
+      for (cur_prop = cur->property_head.next; cur_prop != &cur->property_head; cur_prop = cur_prop->next) {
+        switch (jj) {
+        case 0: 
+          CU_ASSERT(strcmp(cur_prop->name, "display") == 0);
+          CU_ASSERT(strcmp(cur_prop->value, "none") == 0);
+          break;
+        }
+        jj++;
+      }
+      CU_ASSERT(jj == 1);
+      ii++;
+    }
+    CU_ASSERT(ii == 2);
+  }
+
+
+  APR_TERM;
+}
+/*
+ * vim:ts=2 et
+ */