gonzui 1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. data/AUTHORS.txt +9 -0
  2. data/History.txt +5539 -0
  3. data/Manifest.txt +115 -0
  4. data/PostInstall.txt +17 -0
  5. data/README.rdoc +149 -0
  6. data/Rakefile +28 -0
  7. data/bin/gonzui-db +167 -0
  8. data/bin/gonzui-import +177 -0
  9. data/bin/gonzui-remove +58 -0
  10. data/bin/gonzui-search +68 -0
  11. data/bin/gonzui-server +176 -0
  12. data/bin/gonzui-update +53 -0
  13. data/data/gonzui/catalog/catalog.ja +80 -0
  14. data/data/gonzui/doc/favicon.ico +0 -0
  15. data/data/gonzui/doc/folder.png +0 -0
  16. data/data/gonzui/doc/gonzui.css +279 -0
  17. data/data/gonzui/doc/gonzui.js +111 -0
  18. data/data/gonzui/doc/text.png +0 -0
  19. data/data/gonzuirc.sample +29 -0
  20. data/ext/autopack/autopack.c +88 -0
  21. data/ext/autopack/extconf.rb +3 -0
  22. data/ext/delta/delta.c +147 -0
  23. data/ext/delta/extconf.rb +5 -0
  24. data/ext/texttokenizer/extconf.rb +5 -0
  25. data/ext/texttokenizer/texttokenizer.c +93 -0
  26. data/ext/xmlformatter/extconf.rb +5 -0
  27. data/ext/xmlformatter/xmlformatter.c +207 -0
  28. data/lib/gonzui.rb +59 -0
  29. data/lib/gonzui/apt.rb +193 -0
  30. data/lib/gonzui/bdbdbm.rb +118 -0
  31. data/lib/gonzui/cmdapp.rb +14 -0
  32. data/lib/gonzui/cmdapp/app.rb +175 -0
  33. data/lib/gonzui/cmdapp/search.rb +134 -0
  34. data/lib/gonzui/config.rb +117 -0
  35. data/lib/gonzui/content.rb +19 -0
  36. data/lib/gonzui/dbm.rb +673 -0
  37. data/lib/gonzui/deindexer.rb +162 -0
  38. data/lib/gonzui/delta.rb +49 -0
  39. data/lib/gonzui/extractor.rb +347 -0
  40. data/lib/gonzui/fetcher.rb +309 -0
  41. data/lib/gonzui/gettext.rb +144 -0
  42. data/lib/gonzui/importer.rb +84 -0
  43. data/lib/gonzui/indexer.rb +316 -0
  44. data/lib/gonzui/info.rb +80 -0
  45. data/lib/gonzui/license.rb +100 -0
  46. data/lib/gonzui/logger.rb +48 -0
  47. data/lib/gonzui/monitor.rb +177 -0
  48. data/lib/gonzui/progressbar.rb +235 -0
  49. data/lib/gonzui/remover.rb +38 -0
  50. data/lib/gonzui/searcher.rb +330 -0
  51. data/lib/gonzui/searchquery.rb +235 -0
  52. data/lib/gonzui/searchresult.rb +111 -0
  53. data/lib/gonzui/updater.rb +254 -0
  54. data/lib/gonzui/util.rb +415 -0
  55. data/lib/gonzui/vcs.rb +128 -0
  56. data/lib/gonzui/webapp.rb +25 -0
  57. data/lib/gonzui/webapp/advsearch.rb +123 -0
  58. data/lib/gonzui/webapp/filehandler.rb +24 -0
  59. data/lib/gonzui/webapp/jsfeed.rb +61 -0
  60. data/lib/gonzui/webapp/markup.rb +445 -0
  61. data/lib/gonzui/webapp/search.rb +269 -0
  62. data/lib/gonzui/webapp/servlet.rb +319 -0
  63. data/lib/gonzui/webapp/snippet.rb +155 -0
  64. data/lib/gonzui/webapp/source.rb +37 -0
  65. data/lib/gonzui/webapp/stat.rb +137 -0
  66. data/lib/gonzui/webapp/top.rb +63 -0
  67. data/lib/gonzui/webapp/uri.rb +140 -0
  68. data/lib/gonzui/webapp/webrick.rb +48 -0
  69. data/script/console +10 -0
  70. data/script/destroy +14 -0
  71. data/script/generate +14 -0
  72. data/script/makemanifest.rb +21 -0
  73. data/tasks/extconf.rake +13 -0
  74. data/tasks/extconf/autopack.rake +43 -0
  75. data/tasks/extconf/delta.rake +43 -0
  76. data/tasks/extconf/texttokenizer.rake +43 -0
  77. data/tasks/extconf/xmlformatter.rake +43 -0
  78. data/test/_external_tools.rb +13 -0
  79. data/test/_test-util.rb +142 -0
  80. data/test/foo/Makefile.foo +66 -0
  81. data/test/foo/bar.c +5 -0
  82. data/test/foo/bar.h +6 -0
  83. data/test/foo/foo.c +25 -0
  84. data/test/foo/foo.spec +33 -0
  85. data/test/test_apt.rb +42 -0
  86. data/test/test_autopack_extn.rb +7 -0
  87. data/test/test_bdbdbm.rb +79 -0
  88. data/test/test_cmdapp-app.rb +35 -0
  89. data/test/test_cmdapp-search.rb +99 -0
  90. data/test/test_config.rb +28 -0
  91. data/test/test_content.rb +15 -0
  92. data/test/test_dbm.rb +171 -0
  93. data/test/test_deindexer.rb +50 -0
  94. data/test/test_delta.rb +66 -0
  95. data/test/test_extractor.rb +78 -0
  96. data/test/test_fetcher.rb +75 -0
  97. data/test/test_gettext.rb +50 -0
  98. data/test/test_gonzui.rb +11 -0
  99. data/test/test_helper.rb +10 -0
  100. data/test/test_importer.rb +56 -0
  101. data/test/test_indexer.rb +37 -0
  102. data/test/test_info.rb +82 -0
  103. data/test/test_license.rb +49 -0
  104. data/test/test_logger.rb +60 -0
  105. data/test/test_monitor.rb +23 -0
  106. data/test/test_searcher.rb +37 -0
  107. data/test/test_searchquery.rb +27 -0
  108. data/test/test_searchresult.rb +43 -0
  109. data/test/test_texttokenizer.rb +47 -0
  110. data/test/test_updater.rb +95 -0
  111. data/test/test_util.rb +149 -0
  112. data/test/test_vcs.rb +61 -0
  113. data/test/test_webapp-markup.rb +42 -0
  114. data/test/test_webapp-util.rb +19 -0
  115. data/test/test_webapp-xmlformatter.rb +19 -0
  116. metadata +291 -0
Binary file
@@ -0,0 +1,29 @@
1
+ {
2
+ :access_log_file => "access.log",
3
+ :base_mount_point => "",
4
+ :cache_directory => "gonzui.db/cache",
5
+ # :catalog_directory => "catalog",
6
+ :daemon => false,
7
+ :db_cache_size => 5242880,
8
+ :db_directory => "gonzui.db",
9
+ :default_results_per_page => 10,
10
+ # :doc_directory => "doc",
11
+ :encoding_preference => ["iso-2022-jp", "euc-jp", "utf-8", "shift_jis", "cp932", "iso-8859-1", "ascii"],
12
+ :exclude_pattern => /~$|\.bak$|CVS|\.svn|\.git/,
13
+ :gonzui_log_file => "gonzui.log",
14
+ :group => "Administrators",
15
+ :http_port => 46984,
16
+ :max_packages_per_page => 100,
17
+ :max_pages => 20,
18
+ :max_results_per_page => 50,
19
+ :max_words => 10,
20
+ :noindex_formats => [],
21
+ :nresults_candidates => [10, 20, 30, 50],
22
+ :pid_file => "gonzui.pid",
23
+ :quiet => false,
24
+ :site_title => "gonzui",
25
+ # :temporary_directory => "/tmp",
26
+ :user => "Administrator",
27
+ :utf8 => true,
28
+ :verbose => false,
29
+ }
@@ -0,0 +1,88 @@
1
+ /* -*- mode: C; indent-tabs-mode: nil; c-basic-offset: 2 c-style: "BSD" -*- */
2
+ /*
3
+ * autopack.c - C functions for AutoPack module.
4
+ *
5
+ * Copyright (C) 2005 Satoru Takabayashi <satoru@namazu.org>
6
+ * All rights reserved.
7
+ * This is free software with ABSOLUTELY NO WARRANTY.
8
+ *
9
+ * You can redistribute it and/or modify it under the terms of
10
+ * the GNU General Public License version 2.
11
+ */
12
+
13
+ #include <ruby.h>
14
+
15
+ #ifndef RSTRING_PTR
16
+ # define RSTRING_PTR(s) (RSTRING(s)->ptr)
17
+ #endif
18
+
19
+ static VALUE
20
+ rb_autopack_pack_fixnum(VALUE obj, VALUE value)
21
+ {
22
+ int n;
23
+ unsigned char str[4];
24
+ if (TYPE(value) != T_FIXNUM) {
25
+ rb_raise(rb_eTypeError, "Fixnum expected");
26
+ }
27
+ n = FIX2INT(value);
28
+ str[0] = (n >> 24) & 0xff;
29
+ str[1] = (n >> 16) & 0xff;
30
+ str[2] = (n >> 8) & 0xff;
31
+ str[3] = n & 0xff;
32
+ return rb_str_new(str, 4);
33
+ }
34
+
35
+ static VALUE
36
+ rb_autopack_pack_id2(VALUE obj, VALUE id1, VALUE id2)
37
+ {
38
+ int n1, n2;
39
+ unsigned char str[8];
40
+ if (!(TYPE(id1) == T_FIXNUM && TYPE(id2) == T_FIXNUM)) {
41
+ rb_raise(rb_eTypeError, "Fixnum expected");
42
+ }
43
+ n1 = FIX2INT(id1);
44
+ n2 = FIX2INT(id2);
45
+ str[0] = (n1 >> 24) & 0xff;
46
+ str[1] = (n1 >> 16) & 0xff;
47
+ str[2] = (n1 >> 8) & 0xff;
48
+ str[3] = n1 & 0xff;
49
+ str[4] = (n2 >> 24) & 0xff;
50
+ str[5] = (n2 >> 16) & 0xff;
51
+ str[6] = (n2 >> 8) & 0xff;
52
+ str[7] = n2 & 0xff;
53
+ return rb_str_new(str, 8);
54
+ }
55
+
56
+ static VALUE
57
+ rb_autopack_unpack_fixnum(VALUE obj, VALUE value)
58
+ {
59
+ int n;
60
+ unsigned char *str;
61
+ if (TYPE(value) != T_STRING) {
62
+ rb_raise(rb_eTypeError, "String expected");
63
+ }
64
+ str = RSTRING_PTR(value);
65
+ n = (str[0] << 24) + (str[1] << 16) + (str[2] << 8) + str[3];
66
+ return INT2FIX(n);
67
+ }
68
+
69
+ void Init_autopack()
70
+ {
71
+ VALUE mGonzui, mAutoPack;
72
+ mGonzui = rb_define_module("Gonzui");
73
+ mAutoPack = rb_define_module_under(mGonzui, "AutoPack");
74
+
75
+ rb_define_module_function(mAutoPack, "pack_id",
76
+ rb_autopack_pack_fixnum, 1);
77
+ rb_define_module_function(mAutoPack, "unpack_id",
78
+ rb_autopack_unpack_fixnum, 1);
79
+
80
+ rb_define_module_function(mAutoPack, "pack_fixnum",
81
+ rb_autopack_pack_fixnum, 1);
82
+ rb_define_module_function(mAutoPack, "unpack_fixnum",
83
+ rb_autopack_unpack_fixnum, 1);
84
+ rb_define_module_function(mAutoPack, "pack_id2",
85
+ rb_autopack_pack_id2, 2);
86
+ }
87
+
88
+
@@ -0,0 +1,3 @@
1
+ require 'mkmf'
2
+
3
+ create_makefile('gonzui/autopack')
@@ -0,0 +1,147 @@
1
+ /* -*- mode: C; indent-tabs-mode: nil; c-basic-offset: 2 c-style: "BSD" -*- */
2
+ /*
3
+ * delta.c - byte-oriented delta compression implementation
4
+ *
5
+ * Copyright (C) 2005 Satoru Takabayashi <satoru@namazu.org>
6
+ * Copyright (C) 2005 Keisuke Nishida <knishida@open-cobol.org>
7
+ * All rights reserved.
8
+ * This is free software with ABSOLUTELY NO WARRANTY.
9
+ *
10
+ * You can redistribute it and/or modify it under the terms of
11
+ * the GNU General Public License version 2.
12
+ */
13
+
14
+ #include <ruby.h>
15
+ #include <assert.h>
16
+
17
+ #ifndef RARRAY_PTR
18
+ # define RARRAY_PTR(str) (RARRAY(str)->ptr)
19
+ #endif
20
+ #ifndef RARRAY_LEN
21
+ # define RARRAY_LEN(str) (RARRAY(str)->len)
22
+ #endif
23
+
24
+ typedef void (*CodeFunc)(VALUE *p, int i, int *prev);
25
+
26
+ static inline void
27
+ decode(VALUE *p, int i, int *prev)
28
+ {
29
+ int this;
30
+ if (TYPE(p[i]) != T_FIXNUM) {
31
+ rb_raise(rb_eTypeError, "wrong argument type (fixnum required)");
32
+ }
33
+ this = FIX2INT(p[i]);
34
+ p[i] = INT2FIX(this + *prev);
35
+ *prev = FIX2INT(p[i]);
36
+ }
37
+
38
+ static inline void
39
+ encode(VALUE *p, int i, int *prev)
40
+ {
41
+ int this;
42
+ if (TYPE(p[i]) != T_FIXNUM) {
43
+ rb_raise(rb_eTypeError, "wrong argument type (fixnum required)");
44
+ }
45
+
46
+ this = FIX2INT(p[i]);
47
+ p[i] = INT2FIX(this - *prev);
48
+ if (FIX2INT(p[i]) < 0) {
49
+ rb_raise(rb_eArgError, "Encode failed: value becomes minus");
50
+ }
51
+ *prev = this;
52
+ }
53
+
54
+ static VALUE
55
+ rb_delta_code_tuples(VALUE obj, VALUE list,
56
+ VALUE delta_size, VALUE unit_size, CodeFunc code)
57
+ {
58
+ enum { PREV_MAX = 128 };
59
+ int i, j;
60
+ int dsize;
61
+ int usize;
62
+ long len;
63
+ int prev[PREV_MAX];
64
+ VALUE *p;
65
+
66
+ if (!(TYPE(list) == T_ARRAY && TYPE(delta_size) == T_FIXNUM &&
67
+ TYPE(unit_size) == T_FIXNUM && FIX2INT(delta_size) < PREV_MAX ))
68
+ {
69
+ rb_raise(rb_eTypeError, "wrong argument type");
70
+ }
71
+
72
+ dsize = FIX2INT(delta_size);
73
+ usize = FIX2INT(unit_size);
74
+ len = RARRAY_LEN(list);
75
+ if (!(len % usize == 0 && dsize <= usize)) {
76
+ rb_raise(rb_eArgError, "wrong argument size");
77
+ }
78
+ p = RARRAY_PTR(list);
79
+ memset(prev, 0, sizeof(int) * dsize);
80
+ for (i = 0; i < len; i += usize) {
81
+ for (j = 0; j < dsize; j++) {
82
+ code(p, i + j, prev + j);
83
+ }
84
+ }
85
+ return list;
86
+ }
87
+
88
+ static VALUE
89
+ rb_delta_decode_tuples(VALUE obj, VALUE list,
90
+ VALUE delta_size, VALUE unit_size)
91
+ {
92
+ return rb_delta_code_tuples(obj, list, delta_size, unit_size, decode);
93
+ }
94
+
95
+ static VALUE
96
+ rb_delta_encode_tuples(VALUE obj, VALUE list,
97
+ VALUE delta_size, VALUE unit_size)
98
+ {
99
+ return rb_delta_code_tuples(obj, list, delta_size, unit_size, encode);
100
+ }
101
+
102
+ static VALUE
103
+ rb_delta_code_fixnums(VALUE obj, VALUE list, CodeFunc code)
104
+ {
105
+ int i;
106
+ long len;
107
+ VALUE *p;
108
+ int prev = 0;
109
+ if (TYPE(list) != T_ARRAY) {
110
+ rb_raise(rb_eTypeError, "wrong argument type");
111
+ }
112
+ p = RARRAY_PTR(list);
113
+ len = RARRAY_LEN(list);
114
+ for (i = 0; i < len; i++) {
115
+ code(p, i, &prev);
116
+ }
117
+ return list;
118
+ }
119
+
120
+ static VALUE
121
+ rb_delta_decode_fixnums(VALUE obj, VALUE list)
122
+ {
123
+ rb_delta_code_fixnums(obj, list, decode);
124
+ }
125
+
126
+ static VALUE
127
+ rb_delta_encode_fixnums(VALUE obj, VALUE list)
128
+ {
129
+ rb_delta_code_fixnums(obj, list, encode);
130
+ }
131
+
132
+ void Init_delta()
133
+ {
134
+ VALUE mGonzui, mDelta;
135
+ mGonzui = rb_define_module("Gonzui");
136
+ mDelta = rb_define_module_under(mGonzui, "DeltaDumper");
137
+ rb_define_module_function(mDelta, "encode_tuples",
138
+ rb_delta_encode_tuples, 3);
139
+ rb_define_module_function(mDelta, "decode_tuples",
140
+ rb_delta_decode_tuples, 3);
141
+ rb_define_module_function(mDelta, "encode_fixnums",
142
+ rb_delta_encode_fixnums, 1);
143
+ rb_define_module_function(mDelta, "decode_fixnums",
144
+ rb_delta_decode_fixnums, 1);
145
+ }
146
+
147
+
@@ -0,0 +1,5 @@
1
+ require 'mkmf'
2
+
3
+ dir_config("delta")
4
+
5
+ create_makefile("gonzui/delta")
@@ -0,0 +1,5 @@
1
+ require 'mkmf'
2
+
3
+ dir_config("texttokenizer")
4
+
5
+ create_makefile("gonzui/texttokenizer")
@@ -0,0 +1,93 @@
1
+ /* -*- mode: C; indent-tabs-mode: nil; c-basic-offset: 2 c-style: "BSD" -*- */
2
+ /*
3
+ * texttokenizer.c - a simple text tokenizer
4
+ *
5
+ * Copyright (C) 2005 Satoru Takabayashi <satoru@namazu.org>
6
+ * Copyright (C) 2005 Keisuke Nishida <knishida@open-cobol.org>
7
+ * All rights reserved.
8
+ * This is free software with ABSOLUTELY NO WARRANTY.
9
+ *
10
+ * You can redistribute it and/or modify it under the terms of
11
+ * the GNU General Public License version 2.
12
+ *
13
+ *
14
+ */
15
+
16
+ #include <assert.h>
17
+ #include <ruby.h>
18
+
19
+ #ifndef RSTRING_PTR
20
+ # define RSTRING_PTR(s) (RSTRING(s)->ptr)
21
+ #endif
22
+ #ifndef RSTRING_LEN
23
+ # define RSTRING_LEN(s) (RSTRING(s)->len)
24
+ #endif
25
+
26
+ static inline int utf8len(const unsigned char *s, const unsigned char *eot)
27
+ {
28
+ int len = 0;
29
+ if (*s < 0x80) {
30
+ len = 1;
31
+ } else if ((s + 1 < eot) && (*s & 0xe0) == 0xc0) {
32
+ len = 2;
33
+ } else if ((s + 2 < eot) && (*s & 0xf0) == 0xe0) {
34
+ len = 3;
35
+ } else if ((s + 3 < eot) && (*s & 0xf8) == 0xf0) {
36
+ len = 4;
37
+ } else if ((s + 4 < eot) && (*s & 0xfc) == 0xf8) {
38
+ len = 5;
39
+ } else if ((s + 5 < eot) && (*s & 0xfe) == 0xfc) {
40
+ len = 6;
41
+ } else {
42
+ rb_raise(rb_eArgError, "invalid UTF-8 character");
43
+ }
44
+ return len;
45
+ }
46
+
47
+ static inline unsigned char *
48
+ skip(unsigned char *s, unsigned char *eot)
49
+ {
50
+ for (; s < eot; s++)
51
+ if (isalnum(*s) || *s >= 0x80)
52
+ break;
53
+ return s;
54
+ }
55
+
56
+ /*
57
+ * Iterate over each word.
58
+ * word: [a-zA-Z0-9]+ or single multi-byte UTF-8 character
59
+ */
60
+ static VALUE texttokenizer_each_word(VALUE obj, VALUE text)
61
+ {
62
+ VALUE str;
63
+ unsigned char *s, *beg, *eot;
64
+
65
+ str = rb_obj_as_string(text);
66
+ beg = RSTRING_PTR(str);
67
+ eot = beg + RSTRING_LEN(str);
68
+ s = skip(beg, eot);
69
+
70
+ while (s < eot) {
71
+ unsigned char *b = s;
72
+ if (*s >= 0x80) {
73
+ s += utf8len(s, eot);
74
+ } else {
75
+ for (; s < eot; s++)
76
+ if (!((isalnum(*s) || *s == '_')))
77
+ break;
78
+ }
79
+ rb_yield_values(2, rb_str_new(b, s - b), INT2FIX(b - beg));
80
+ s = skip(s, eot);
81
+ }
82
+ return Qnil;
83
+ }
84
+
85
+ void Init_texttokenizer()
86
+ {
87
+ VALUE mGonzui, mTextTokenizer;
88
+ mGonzui = rb_define_module("Gonzui");
89
+ mTextTokenizer = rb_define_module_under(mGonzui, "TextTokenizer");
90
+
91
+ rb_define_module_function(mTextTokenizer, "each_word",
92
+ texttokenizer_each_word, 1);
93
+ }
@@ -0,0 +1,5 @@
1
+ require 'mkmf'
2
+
3
+ dir_config("xmlformatter")
4
+
5
+ create_makefile("gonzui/webapp/xmlformatter")
@@ -0,0 +1,207 @@
1
+ /* -*- mode: C; indent-tabs-mode: nil; c-basic-offset: 2 c-style: "BSD" -*- */
2
+ /*
3
+ * xmlformatter.c - a simple class for generating XML texts
4
+ *
5
+ * Copyright (C) 2004-2005 Keisuke Nishida <knishida@open-cobol.org>
6
+ * All rights reserved.
7
+ * This is free software with ABSOLUTELY NO WARRANTY.
8
+ *
9
+ * You can redistribute it and/or modify it under the terms of
10
+ * the GNU General Public License version 2.
11
+ */
12
+
13
+ #include <ruby.h>
14
+ #include <st.h>
15
+
16
+ #define BLOCK_SIZE (128 * 1024) /* 128 KB */
17
+
18
+ #ifndef RARRAY_PTR
19
+ # define RARRAY_PTR(str) (RARRAY(str)->ptr)
20
+ #endif
21
+ #ifndef RARRAY_LEN
22
+ # define RARRAY_LEN(str) (RARRAY(str)->len)
23
+ #endif
24
+
25
+ #ifndef RSTRING_PTR
26
+ # define RSTRING_PTR(s) (RSTRING(s)->ptr)
27
+ #endif
28
+ #ifndef RSTRING_LEN
29
+ # define RSTRING_LEN(s) (RSTRING(s)->len)
30
+ #endif
31
+
32
+ typedef struct gonzui_xmlformatter {
33
+ char *data;
34
+ size_t size;
35
+ size_t max_size;
36
+ } gonzui_xmlformatter_t;
37
+
38
+ static gonzui_xmlformatter_t *xmlformatter_new(void)
39
+ {
40
+ gonzui_xmlformatter_t *xf = malloc(sizeof(gonzui_xmlformatter_t));
41
+ memset(xf, 0, sizeof(gonzui_xmlformatter_t));
42
+ xf->max_size = BLOCK_SIZE;
43
+ xf->size = 0;
44
+ xf->data = malloc(xf->max_size);
45
+ return xf;
46
+ }
47
+
48
+ static void xmlformatter_write(gonzui_xmlformatter_t *xf, char *data)
49
+ {
50
+ long size = strlen(data);
51
+ if (xf->size + size > xf->max_size) {
52
+ xf->max_size += BLOCK_SIZE;
53
+ xf->data = realloc(xf->data, xf->max_size);
54
+ }
55
+ memcpy(xf->data + xf->size, data, size);
56
+ xf->size += size;
57
+ }
58
+
59
+ static void xmlformatter_write_obj(gonzui_xmlformatter_t *xf, VALUE obj)
60
+ {
61
+ char *p;
62
+ char *data;
63
+ size_t size;
64
+ if (SYMBOL_P(obj)) {
65
+ data = rb_id2name(SYM2ID(obj));
66
+ size = strlen(data);
67
+ } else {
68
+ VALUE s = rb_obj_as_string(obj);
69
+ data = RSTRING_PTR(s);
70
+ size = RSTRING_LEN(s);
71
+ }
72
+ if (xf->size + size * 6 > xf->max_size) {
73
+ xf->max_size += BLOCK_SIZE;
74
+ xf->data = realloc(xf->data, xf->max_size);
75
+ }
76
+ for (p = data; *p; p++) {
77
+ int c = *p;
78
+ if (c == '<') {
79
+ memcpy(xf->data + xf->size, "&lt;", 4);
80
+ xf->size += 4;
81
+ } else if (c == '>') {
82
+ memcpy(xf->data + xf->size, "&gt;", 4);
83
+ xf->size += 4;
84
+ } else if (c == '&') {
85
+ memcpy(xf->data + xf->size, "&amp;", 5);
86
+ xf->size += 5;
87
+ } else if (c == '"') {
88
+ memcpy(xf->data + xf->size, "&quot;", 6);
89
+ xf->size += 6;
90
+ } else {
91
+ xf->data[xf->size++] = c;
92
+ }
93
+ }
94
+ }
95
+
96
+ static void xmlformatter_free(gonzui_xmlformatter_t *xf)
97
+ {
98
+ if (xf == NULL)
99
+ return;
100
+ free(xf->data);
101
+ free(xf);
102
+ }
103
+
104
+ static VALUE xmlformatter_s_allocate(VALUE klass)
105
+ {
106
+ return Data_Wrap_Struct(klass, NULL, xmlformatter_free, NULL);
107
+ }
108
+
109
+ static VALUE xmlformatter_initialize(VALUE self)
110
+ {
111
+ gonzui_xmlformatter_t *xf;
112
+ Data_Get_Struct(self, gonzui_xmlformatter_t, xf);
113
+ if (xf)
114
+ rb_raise(rb_eArgError, "called twice");
115
+
116
+ DATA_PTR(self) = xmlformatter_new();
117
+ return self;
118
+ }
119
+
120
+ static VALUE xmlformatter_add_xml_declaration(VALUE self)
121
+ {
122
+ gonzui_xmlformatter_t *xf;
123
+ Data_Get_Struct(self, gonzui_xmlformatter_t, xf);
124
+ if (xf)
125
+ xmlformatter_write(xf, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
126
+ return Qnil;
127
+ }
128
+
129
+ static VALUE xmlformatter_add_doctype(VALUE self)
130
+ {
131
+ gonzui_xmlformatter_t *xf;
132
+ Data_Get_Struct(self, gonzui_xmlformatter_t, xf);
133
+ if (xf)
134
+ xmlformatter_write(xf, "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n"
135
+ " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n");
136
+ return Qnil;
137
+ }
138
+
139
+ static int format_hash(VALUE key, VALUE val, gonzui_xmlformatter_t *xf)
140
+ {
141
+ xmlformatter_write(xf, " ");
142
+ xmlformatter_write_obj(xf, key);
143
+ xmlformatter_write(xf, "=\"");
144
+ xmlformatter_write_obj(xf, val);
145
+ xmlformatter_write(xf, "\"");
146
+ return ST_CONTINUE;
147
+ }
148
+
149
+ static void format(gonzui_xmlformatter_t *xf, VALUE xml)
150
+ {
151
+ if (rb_type(xml) == T_ARRAY) {
152
+ /* array */
153
+ long i = 0;
154
+ long len = RARRAY_LEN(xml);
155
+ VALUE *a = RARRAY_PTR(xml);
156
+ VALUE tag;
157
+
158
+ if (len == 0)
159
+ rb_raise(rb_eArgError, "too short");
160
+
161
+ /* start tag */
162
+ tag = a[i++];
163
+ xmlformatter_write(xf, "<");
164
+ xmlformatter_write_obj(xf, tag);
165
+
166
+ /* attributes */
167
+ if (i < len && rb_type(a[i]) == T_HASH)
168
+ st_foreach(RHASH(a[i++])->tbl, format_hash, (st_data_t)xf);
169
+
170
+ /* body, end tag */
171
+ if (i >= len) {
172
+ xmlformatter_write(xf, "\n/>");
173
+ } else {
174
+ xmlformatter_write(xf, "\n>");
175
+ for (; i < len; i++)
176
+ format(xf, a[i]);
177
+ xmlformatter_write(xf, "</");
178
+ xmlformatter_write_obj(xf, tag);
179
+ xmlformatter_write(xf, "\n>");
180
+ }
181
+ } else {
182
+ /* other nobject */
183
+ xmlformatter_write_obj(xf, xml);
184
+ }
185
+ }
186
+
187
+ static VALUE xmlformatter_format(VALUE self, VALUE xml)
188
+ {
189
+ gonzui_xmlformatter_t *xf;
190
+ Data_Get_Struct(self, gonzui_xmlformatter_t, xf);
191
+ if (xf == NULL)
192
+ return Qnil;
193
+ Check_Type(xml, T_ARRAY);
194
+ format(xf, xml);
195
+ return rb_str_new(xf->data, xf->size);
196
+ }
197
+
198
+ void Init_xmlformatter()
199
+ {
200
+ VALUE Gonzui = rb_define_module("Gonzui");
201
+ VALUE XMLFormatter = rb_define_class_under(Gonzui, "XMLFormatter", rb_cData);
202
+ rb_define_alloc_func(XMLFormatter, xmlformatter_s_allocate);
203
+ rb_define_method(XMLFormatter, "initialize", xmlformatter_initialize, 0);
204
+ rb_define_method(XMLFormatter, "add_xml_declaration", xmlformatter_add_xml_declaration, 0);
205
+ rb_define_method(XMLFormatter, "add_doctype", xmlformatter_add_doctype, 0);
206
+ rb_define_method(XMLFormatter, "format", xmlformatter_format, 1);
207
+ }