メモ代わり。てきとーに。 いや、ですからてきとーですって。 2年前ぐらいにPythonあたりでメールくれた方、ごめんなさい。メール紛失してしまい無視した形になってしまいました。。。

2008年5月17日土曜日

[mod_chxj] css部分をざっくり考え中

個人的メモ。

CSS関連でやるべきことは、

  • CSSEngineOn/CSSEngineOff機能の追加
  • CSS変換エンジンの実装
ぐらいか。

## データ構造
### property型
* name
* value

### selector型
* name (tag, class, id)
* propertyのリスト


### stylesheet型
* selectorのリストを保持する。
* linkタグがあった場合、該当CSSを読み込み、同一セレクタが既にある場合には上書きする。
* linkタグがあった場合、該当CSSを読み込み、同一セレクタが無い場合は新規にselectorを追加する。
* @importルールがあった場合、該当CSSを読み込み、同一セレクタが既にある場合には、該当セレクタのプロパティだけ
を更新する。
* @importルールがあった場合、該当CSSを読み込み、同一セレクタが無い場合には、新規にselectorを追加する。

### current_stylesheet型
* 現在有効なプロパティのリストを保持する。
* 各プロパティは静的にもつ。
* 定義されていないプロパティについては、nullが設定されている。

### current_stylesheetスタック
* 現在有効なcurrent_stylesheet型を要素とするスタック。
* タグがオープンされると、現在最上位にあるcurrent_stylesheetをコピーして新しいcurrent_stylesheetを
作成し、current_stylesheetにpushする。
(extended_environment)
* タグがクローズされると、current_stylesheetスタックをpopする。

## 各コンバータでの処理
1) タグがオープンされる。
1-1) current_stylesheet型領域を確保する。
1-2) current_stylesheet型領域にcurrent_stylesheetスタックの最上位(if any)のプロパティを設定する。
1-3) オープンされたタグ名、クラス名、ID名により、該当selectorをstylesheet型から探す。
1-4) もしあれば、該当selectorのプロパティでcurrent_stylesheetのプロパティを更新する。
1-5) current_stylesheetをcurrent_stylesheetスタックにpushする。

2) タグ内の処理
2-1) style属性があれば、style属性のvalue値をパースする。
2-2) パース結果でcurrent_stylesheetスタックの最上位にあるcurrent_stylesheetのプロパティを更新する。

3) 各端末用タグの生成
3-1) current_stylesheetを考慮したうえで、各端末用にタグを出力する。
DoCoMoであればstyle属性出力等。

4) タグがクローズされる。
4-1) current_stylesheetスタックから1要素popする。

# selectorの特定方法
## class,IDが指定されている場合
1) tag.class#IDを検索する

2) tag#IDを検索する

3) #IDを検索する

4) 「classのみが指定されている場合」へ

IDのみが指定されている場合は、
1) tag#IDを検索する

2) #IDを検索する

3) 「tagのみが指定されている場合」へ

classのみが指定されている場合
1) tag.classを検索する
2) .classを検索する
3) 「tagのみが指定されている場合」へ

tagのみが指定されている場合
1) tagを検索する
2) *を検索する

同一定義があった場合は後に書かれているものが優先。


うーむ・・・。
.

[mod_chxj] libcrocoを使う方向で

CSSパーサにはlibcrocoを使う方向で進めようと思う。

なのでrequiresにはGLibとlibcrocoが追加予定。

うーむ。悩ましい。。
.

2008年5月16日金曜日

[libcroco] CSSパーサ、マニピュレータのlibcrocoを使ってみた

libcrocoをSACのみ使ってみたメモ。

libcrocoとは
GNOMEで使われているC言語で書かれたCSSパーサ、CSS操作ツールキット。
本家(freespiders.org)に接続できない・・・。もしかして、開発停止???
GNOMEのvcsでは、6週間前にコミットされているので停止されているということは無い・・・といいな。

使いそうな構造体
まず、使用しそうな構造体を列挙する。

CRParser
パーサをあらわす。内部構造には基本的に触れられない。
この構造体を使用してパースする。

CRDocHandler
イベントが発生するとコールされるハンドラを登録できる。
登録できるハンドラは以下のとおり。


void (*start_document) (CRDocHandler *a_this);ドキュメントの開始時にコールされる
void (*end_document) (CRDocHandler *a_this) ;ドキュメントが終了したときにコールされる
void (*charset) (CRDocHandler *a_this, CRString *a_charset, CRParsingLocation *a_charset_sym_location) ;キャラクタルールが見つかったときにコールされる。
void (*import_style) (CRDocHandler *a_this, GList *a_media_list, CRString *a_uri, CRString *a_uri_default_ns, CRParsingLocation *a_location) ;import文があったときにコールされる。
void (*import_style_result) (CRDocHandler *a_this, GList *a_media_list, CRString *a_uri, CRString *a_uri_default_ns, CRStyleSheet *a_sheet) ;??
void (*namespace_declaration) (CRDocHandler *a_this, CRString *a_prefix, CRString *a_uri, CRParsingLocation *a_location) ;namespace宣言があった場合にコールされる。
void (*comment) (CRDocHandler *a_this, CRString *a_comment) ;コメントがあった場合にコールされる。
void (*start_selector) (CRDocHandler * a_this, CRSelector *a_selector_list) ;ルール開始時にコールされる。 a_selector_listにはルールに前にあるセレクタのリストが渡される。
void (*end_selector) (CRDocHandler *a_this, CRSelector *a_selector_list) ;ルールの終了を通知する。
void (*property) (CRDocHandler *a_this, CRString *a_name, CRTerm *a_expression, gboolean a_is_important) ;プロパティ宣言がある度にコールされる。
a_nameにはプロパティ名
a_expressionには式
void (*start_font_face) (CRDocHandler *a_this, CRParsingLocation *a_location) ;@font-face文が開始したときにコールされる。
void (*end_font_face) (CRDocHandler *a_this) ; @font-face文が終了したときにコールされる。
void (*start_media) (CRDocHandler *a_this, GList *a_media_list, CRParsingLocation *a_location);media文が開始したときにコールされる。
void (*end_media) (CRDocHandler *a_this, GList *a_media_list) ;media文が終了したときにコールされる。
void (*start_page) (CRDocHandler *a_this, CRString *a_name, CRString *a_pseudo_page, CRParsingLocation *a_location) ;@page文が開始したときにコールされる。
void (*end_page) (CRDocHandler *a_this, CRString *a_name, CRString *pseudo_page) ;@page文が終了したときにコールされる。
void (*ignorable_at_rule) (CRDocHandler *a_this, CRString *a_name) ;知らない@ルールがあった場合にコールされる。
void (*error) (CRDocHandler *a_this) ;パースエラーが起きたときにコールされる。
void (*unrecoverable_error) (CRDocHandler *a_this) ;復帰できないエラーがおきたときにコールされる。



CRSelector
セレクタを表現する構造体。

CRTerm
項を表現する構造体。

CRString
文字列を表現する構造体。



処理の大まかな流れ
大まかな流れは以下のとおり。
  1. CRParserを生成する。
  2. CRDocHandlerを生成する。
  3. CRDocHandlerにハンドラを登録する。
  4. CRDocHandlerをCRParserに登録する。
  5. パースする。
  6. CRParserを解放する。(CRDocHandlerはCRParser解放時に解放される)

使いそうな関数
libcrocoの中で使用しそうな関数は以下のとおり。

* CRParser *cr_parser_new_from_file(const guchar * a_file_uri, enum CREncoding a_enc)
a_file_uriに指定されたファイルをパースするパーサを生成する。
- a_file_uri - オープンするファイルのURIを指定する。
- a_enc - エンコードを指定する。 CR_UCS_4、CR_UCS_1、CR_ISO_8859_1、 CR_ASCII、CR_UTF_8、CR_UTF_16、CR_AUTOを指定できる。

* CRParser *cr_parser_new_from_input(CRInput * a_input)
a_inputに指定された入力ストリームをパースするパーサを生成する。
- a_input - 入力ストリームを指定する。

* CRDocHandler *cr_doc_handler_new (void)
CRDocHandlerを生成する。

* enum CRStatus cr_parser_set_sac_handler (CRParser * a_this, CRDocHandler * a_handler)
CRDocHandlerをCRParserに登録する。
- a_this - 登録先CRParserを指定する。
- a_handler - 登録するCRDocHandlerを指定する。

* enum CRStatus cr_parser_parse (CRParser * a_this)
パースする。
- a_this - cr_parse_new_from_file()やcr_parser_new_from_input()で生成されたCRParserを指定する。

* void cr_parser_destroy (CRParser * a_this)
cr_parser_new_from_file()やcr_parser_new_from_input()で生成されたCRParserを破棄する。
- a_this - cr_parse_new_from_file()やcr_parser_new_from_input()で生成されたCRParserを指定する。

* gboolean cr_doc_handler_unref (CRDocHandler * a_this)
cr_doc_handler_new()で生成されたCRDocHandlerを破棄する。通常cr_parse_destroy()の中で破棄しているため、必要ない。
- a_this - 破棄すべきCRDocHandlerを指定する。

* guchar * cr_term_to_string (CRTerm *a_this) ;
CRTermを文字列に変換する。

* guchar * cr_term_one_to_string (CRTerm * a_this) ;
CRTermを文字列に変換する。

* int cr_term_nr_values (CRTerm *a_this)
CRTermを数値に変換する。


* const gchar * cr_string_peek_raw_str (CRString *a_this);
CRStringをgchar*に変換する。
 



早速なんか作ってみる
a.cssを読み込み、セレクタがあればセレクタを表示。プロパティがあればプロパティを表示するだけの
しょぼいプログラムを作る。

まず、CRParserを生成する。

CRParser *parser = NULL;

parser = cr_parser_new_from_file((unsigned char*)"a.css", CR_ASCII);
if (!parser) {
/* パーサを作成できなかった! */
fprintf(stderr, "parser生成失敗\n");
return -1;
}
 

な感じで生成できる。
cr_parser_new_from_fileの第二引数には
CR_UCS_4、CR_UCS_1、CR_ISO_8859_1、 CR_ASCII、CR_UTF_8、CR_UTF_16、CR_AUTO
しか指定できない。なので、Shift_JISやEUCはダメ。

次にCRDocHandlerを生成する。

CRDocHandler *sac_handler = NULL;

sac_handler = cr_doc_handler_new();
if (!sac_handler) {
fprintf(stderr, "sac_handler生成失敗\n");
return -2;
}
 

な感じで生成。

次は、ハンドラを定義する。
ハンドラは、start_selector、end_selector、propertyで良いはず。
ということで、start_selector、end_selector、propertyを実装する。

static void
s_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;
}
}
}
}
}

static void
s_end_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("end selector:[%s]\n", tmp_str);
g_free (tmp_str);
tmp_str = NULL;
}
}
}
}
}

static void
s_property(CRDocHandler *a_this, CRString *a_name, CRTerm *a_expression, gboolean a_is_important)
{
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;
}
}
}
 

こんな感じでよいと思う。

で、定義したハンドラをCRDocHandlerに登録する。

sac_handler->start_selector = s_start_selector;
sac_handler->end_selector = s_end_selector;
sac_handler->property = s_property;
 


そして、sac_handlerをparserに登録。

/* sac_handlerをparserに登録 */
ret = cr_parser_set_sac_handler(parser, sac_handler);
if (ret != CR_OK) {
fprintf(stderr, "なんかエラーが起きた:[%d]\n", ret);
cr_parser_destroy(parser);
return -3;
}
 


実パース処理。

/* パース */
ret = cr_parser_parse(parser);
if (ret != CR_OK) {
fprintf(stderr, "なんかエラーが起きた:[%d]\n", ret);
cr_parser_destroy(parser);
return -4;
}
 


で終了処理。parserを解放する。

/* 解放 */
cr_parser_destroy(parser);
 

examplesでは、sac_handlerもここで解放しているが、特に解放する必要はない。
cr_parser_destroy()の中でparserに登録されているsac_handlerは解放しているため。


全体のソースは以下な感じ。

#include <stdio.h>
#include <libcroco/libcroco.h>

/*
* gcc -g -Wall -o test01 `croco-0.6-config --cflags` `croco-0.6-config --libs` a.c
*/
static void
s_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;
}
}
}
}
}

static void
s_end_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("end selector:[%s]\n", tmp_str);
g_free (tmp_str);
tmp_str = NULL;
}
}
}
}
}

static void
s_property(CRDocHandler *a_this, CRString *a_name, CRTerm *a_expression, gboolean a_is_important)
{
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;
}
}
}

int
main (int argc, char *argv[])
{
CRParser *parser = NULL ;
CRDocHandler *sac_handler = NULL ;
enum CRStatus ret;

/* parserオブジェクト生成 */
parser = cr_parser_new_from_file ((unsigned char *)"./a.css", CR_ASCII) ;
if (!parser) {
fprintf(stderr, "parserオブジェクト失敗\n");
return -1;
}

/* ハンドラ生成 */
sac_handler = cr_doc_handler_new();
if (!sac_handler) {
fprintf(stderr, "sac_handlerオブジェクト失敗\n");
cr_parser_destroy(parser);
return -2;
}

/* ハンドラ登録 */
sac_handler->start_selector = s_start_selector;
sac_handler->end_selector = s_end_selector;
sac_handler->property = s_property;

/* sac_handlerをparserに登録 */
ret = cr_parser_set_sac_handler(parser, sac_handler);
if (ret != CR_OK) {
fprintf(stderr, "なんかエラーが起きた:[%d]\n", ret);
cr_parser_destroy(parser);
return -3;
}


/* パース */
ret = cr_parser_parse(parser);
if (ret != CR_OK) {
fprintf(stderr, "なんかエラーが起きた:[%d]\n", ret);
cr_parser_destroy(parser);
return -4;
}


/* 解放 */
cr_parser_destroy(parser);
return 0;
}

 



コンパイル
debianであれば、

gcc -g -Wall -o test01 `croco-0.6-config --cflags` `croco-0.6-config --libs` a.c
 

でOK。
a.cはソースファイル名のつもり。


実行してみる。
実行してみるcssは、以下のとおり。

html {
display: block
}
 


実行。

$ ./sac-example-1
$ ./sac-example-1
start selector:[html]
property: name:[display]
property: value:[block]
end selector:[html]
 


うむ。とりあえずできた。

.

[debian][valgrind] debianのvalgrind

世の中、debian/ssl関連で大騒ぎっぽい。
それはおいといて、debian-etch でvalgrindを実行すると、
libpthread-0.10.soの中でメモリリークのエラーが出る。
(注:多分、自分の環境だけ。他の人は多分出ない、に違いない)

まぁいいやっていうことでほっといたんだけど、無視するようにしてみた。
valgrindでチェック対象からはずすには--suppressionsオプションを使用する。


$ valgrind --suppressions=suppファイル名 ターゲット
 

なんてなふうに指定する。

suppファイルは、/usr/lib/valgrind/*以下にいろいろあるのだが、
いい感じのものがデフォルトで入っていない(この時点でなんかおかしい・・・?)

ということで、/usr/lib/valgrind/*のファイルを参考にsuppファイルを作成した。
以下suppファイルの内容。

{
Debian libpthread hack - pthread_initialize_minimal
Memcheck:Leak
fun:calloc
obj:/lib/ld-2.3.*.so
obj:/lib/ld-2.3.*.so
fun:_dl_allocate_tls
fun:__pthread_initialize_minimal
}
{
Debian libpthread hack - pthread_initialize_minimal2
Memcheck:Leak
fun:memalign
obj:/lib/ld-2.3.*.so
fun:_dl_allocate_tls
fun:__pthread_initialize_minimal
}
 


あとは、~/.valgrindrcに

--suppressions=上記で作成したsuppファイル名
 

を指定して完了。
これで余計なエラーが出なくなった。


--
追記
ちなみにpthreadのメモリリークエラーが出てしまうコードは以下のとおり。

#include <stdio.h>

int
main()
{
return 0;
}
 

・・・。
.

2008年5月15日木曜日

[css][mod_chxj] 探し中・・・

CSSパーサにlibcrocoを使おうと思ったが、libcrocoってGNOMEのライブラリからか
glibをリンクする。

うーん。
なんかものものしいなぁ。

既にものものしいんでこれ以上requiresを増やしたくないなー。

うーん。
.

2008年5月12日月曜日

[git][mod_chxj] クローンしなおし・・・(Subversionからgit clone)

trunkを0系にしようと思って、svn delete trunkとやってしまったら、
git cloneしていたところが変になった。(当然か)

ということで、再クローン。

なんだか毎回同じことを調べている気がするのでメモ。


$ git svn clone -T trunk -b branches -t tags \
svn+ssh://konn@svn.sourceforge.jp/svnroot/modchxj/mod_chxj
 

でカレントディレクトリ以下にmod_chxjディレクトリが作成される。

.

[mod_chxj] 0.12.0リリース

昨晩0.12.0をリリースしました。

これで<font size>が使えるようになるかと思います。
(だいぶ時間かかっちゃいました・・・)

追加した内容は以下のとおり。
変換対象に以下のタグ、属性を追加

  • bodyタグ alink属性
  • bodyタグ vlink属性
  • dirタグ type属性
  • fontタグ size属性
  • hrタグ color属性
  • marqueeタグ bgcolor属性
  • ul type属性
  • chxj:ifのlang属性に"jxhtml"を追加。
  • apr_memcacheを廃止し、libmemcachedを使用するようにした。

あとは、バグ修正。
ちなみにdebパッケージはsarge版もetch版も両方ともlibmemcachedとmysqlが使用できるように
コンパイルしたもので、libmemcachedはスタティックリンクしています。
libmemcachedはapt-getで、デフォルトではインストールできないため。

etch版、sarge版ともに

 # dpkg -i ファイル名.deb
 # apt-get -f install
 

で、依存関係から何から何まで解決するかと思います。
device_data.xml、emoji.xmlはインストールしません。
/usr/share/doc/libapache2-mod-chxj/examples/
以下にサンプルがあるのでとりあえずという場合はサンプルを使えばよいかと思います。

次バージョンはいよいよXHTML対応に突入します。
メインは多分CSSまわりになりそう。
あとは、memcachedサーバを複数台設定できるようにするまでできたらよいかと。

.