libxml-ruby 2.4.0 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/HISTORY CHANGED
@@ -1,5 +1,19 @@
1
1
  = Release History
2
2
 
3
+ == 2.5.0 / 2013-01-27 Charlie Savage
4
+
5
+ * Compatibility with older versions for IO::write (rb_io_bufwrite is specific to ruby >= 1.9.3?)
6
+ and symbols are VALUE not ID (julp).
7
+ * Fix version for xmlTextWriterSetQuoteChar, it appeared in libxml2 2.9.0, last version (julp)
8
+ * Update use of LIBXML_VERSION (julp).
9
+ * Fix misuse of rb_scan_args (julp).
10
+ * Update documentation, including DTD docs and added XML Writer (julp).
11
+ * Added a new XML::Writer class (julp).
12
+ * Improved xml reader api and add namespace support. Note that passing a numeric value
13
+ to XML::Reader::move_to_attribute has been deprecated. Instead you should now
14
+ use #move_to_attribute_no. (julp).
15
+ * Improve error handling and messages (Jarl Friis)
16
+
3
17
  == 2.4.0 / 2012-12-14 Charlie Savage
4
18
 
5
19
  * Support libxml 2.9.0 (Daniel Veillard)
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2008-2011 Charlie Savage and contributors
1
+ Copyright (c) 2008-2013 Charlie Savage and contributors
2
2
  Copyright (c) 2002-2007 Sean Chittenden and contributors
3
3
  Copyright (c) 2001 Wai-Sun "Squidster" Chia
4
4
 
@@ -30,14 +30,15 @@ The easiest way to install libxml-ruby is via Ruby Gems. To install:
30
30
  <tt>gem install libxml-ruby</tt>
31
31
 
32
32
  If you are running Windows, make sure to install the Win32 RubyGem
33
- which includes prebuilt extensions for Ruby 1.8 and Ruby 1.9. These
34
- extensions are built with MinGW32 against libxml2 version 2.7.8,
33
+ which includes prebuilt extensions for Ruby 1.8, 1.9 and 2.0 preview. These
34
+ extensions are built with MinGW32 against libxml2 version 2.9.0,
35
35
  iconv version 1.13 and zlib version 1.2.5. Note these binaries
36
36
  are available in the lib\libs directory. To use them, put them
37
37
  someplace on your path.
38
38
 
39
- The gem also includes a Microsoft VC++ 2010
40
- solution (useful for debugging).
39
+ The gem also includes a Microsoft VC++ 2012 solution (useful for debugging).
40
+
41
+ libxml-ruby's source codes lives on Github at https://github.com/xml4r/libxml-ruby.
41
42
 
42
43
  == Getting Started
43
44
  Using libxml is easy. First decide what parser you want to use:
@@ -72,11 +73,11 @@ This can be done using libxml's powerful set of validators:
72
73
 
73
74
  Finally, if you'd like to use XSL Transformations to process data,
74
75
  then install the libxslt gem which is available at
75
- http://rubyforge.org/projects/libxsl/.
76
+ https://github.com/xml4r/libxslt-ruby.
76
77
 
77
78
  == Usage
78
79
  For information about using libxml-ruby please refer to its documentation at
79
- http://xml4r.github.com/libxml-ruby/rdoc/index.html Some tutorials are also
80
+ http://xml4r.github.com/libxml-ruby/rdoc/index.html. Some tutorials are also
80
81
  available at https://github.com/xml4r/libxml-ruby/wiki.
81
82
 
82
83
  All libxml classes are in the LibXML::XML module. The easiest
@@ -165,25 +166,19 @@ From https://svn.concord.org/svn/projects/trunk/common/ruby/xml_benchmarks/
165
166
  Documentation is available via rdoc, and is installed automatically with the
166
167
  gem.
167
168
 
168
- libxml-ruby's online documentation is generated using Hanna. To generate
169
- documentation from source:
170
-
171
- gem install hanna
172
- rake rdoc
169
+ libxml-ruby's online documentation is generated using Hanna, which is a
170
+ development gem dependency.
173
171
 
174
172
  Note that older versions of Rdoc, which ship with Ruby 1.8.x, will report
175
- a number of errors. To avoid them, install Rdoc 2.1 or higher from
176
- RubyForge (http://rdoc.rubyforge.org/). Once you have installed the gem,
177
- you'll have to disable the version of Rdoc that Ruby 1.8.x includes. An
178
- easy way to do that is rename the directory uby/lib/ruby/1.8/rdoc to
173
+ a number of errors. To avoid them, install Rdoc 2.1 or higher. Once you have
174
+ installed the gem, you'll have to disable the version of Rdoc that Ruby 1.8.x
175
+ includes. An easy way to do that is rename the directory ruby/lib/ruby/1.8/rdoc to
179
176
  ruby/lib/ruby/1.8/rdoc_old.
180
177
 
181
178
  == Support
182
179
 
183
- If you have any questions about using libxml-ruby, please send them to
184
- libxml-devel@rubyforge.org. If you have found any bugs in libxml-devel,
185
- or have developed new patches, please submit them to Git Hub at
186
- https://github.com/xml4r/libxml-ruby/issues.
180
+ If you have any questions about using libxml-ruby, please report them to
181
+ Git Hub at https://github.com/xml4r/libxml-ruby/issues
187
182
 
188
183
  == License
189
184
  See LICENSE for license information.
data/Rakefile CHANGED
@@ -53,10 +53,12 @@ end
53
53
  # RDoc Task
54
54
  desc "Generate rdoc documentation"
55
55
  RDoc::Task.new("rdoc") do |rdoc|
56
- rdoc.rdoc_dir = 'doc/libxml-ruby/rdoc'
56
+ rdoc.rdoc_dir = 'website/_site/rdoc'
57
57
  rdoc.title = "LibXML"
58
58
  # Show source inline with line numbers
59
59
  rdoc.options << "--line-numbers"
60
+ rdoc.options << "--charset=utf-8"
61
+ rdoc.options << "--format=hanna"
60
62
  # Make the readme file the start page for the generated html
61
63
  rdoc.main = 'README.rdoc'
62
64
  rdoc.rdoc_files.include('doc/*.rdoc',
@@ -73,7 +75,4 @@ end
73
75
  Rake::TestTask.new do |t|
74
76
  t.libs << "test"
75
77
  t.verbose = true
76
- end
77
-
78
- desc "Build docs, and publish the website"
79
- task :publish_with_docs => [:rdoc, :publish]
78
+ end
@@ -103,6 +103,8 @@ need libxml2.
103
103
  EOL
104
104
  end
105
105
 
106
+ have_func('rb_io_bufwrite', 'ruby/io.h')
107
+
106
108
  # For FreeBSD add /usr/local/include
107
109
  $INCFLAGS << " -I/usr/local/include"
108
110
 
@@ -72,4 +72,5 @@ void Init_libxml_ruby(void)
72
72
  rxml_init_schema();
73
73
  rxml_init_relaxng();
74
74
  rxml_init_reader();
75
+ rxml_init_writer();
75
76
  }
@@ -55,6 +55,7 @@
55
55
  #include "ruby_xml_html_parser_options.h"
56
56
  #include "ruby_xml_html_parser_context.h"
57
57
  #include "ruby_xml_reader.h"
58
+ #include "ruby_xml_writer.h"
58
59
  #include "ruby_xml_sax2_handler.h"
59
60
  #include "ruby_xml_sax_parser.h"
60
61
  #include "ruby_xml_xinclude.h"
@@ -1,8 +1,12 @@
1
1
  /* Please see the LICENSE file for copyright and distribution information */
2
2
 
3
+ #include "extconf.h"
3
4
  #include "ruby_libxml.h"
4
5
 
5
6
  static ID READ_METHOD;
7
+ #ifndef HAVE_RB_IO_BUFWRITE
8
+ static ID WRITE_METHOD;
9
+ #endif /* !HAVE_RB_IO_BUFWRITE */
6
10
 
7
11
  /* This method is called by libxml when it wants to read
8
12
  more data from a stream. We go with the duck typing
@@ -22,7 +26,25 @@ int rxml_read_callback(void *context, char *buffer, int len)
22
26
  return size;
23
27
  }
24
28
 
29
+ int rxml_write_callback(void *context, const char *buffer, int len)
30
+ {
31
+ #ifndef HAVE_RB_IO_BUFWRITE
32
+ VALUE io, written, string;
33
+
34
+ io = (VALUE) context;
35
+ string = rb_str_new(buffer, len);
36
+ written = rb_funcall(io, WRITE_METHOD, 1, string);
37
+
38
+ return NUM2INT(written);
39
+ #else
40
+ return rb_io_bufwrite((VALUE) context, buffer, (size_t) len);
41
+ #endif /* !HAVE_RB_IO_BUFWRITE */
42
+ }
43
+
25
44
  void rxml_init_io(void)
26
45
  {
27
46
  READ_METHOD = rb_intern("read");
47
+ #ifndef HAVE_RB_IO_BUFWRITE
48
+ WRITE_METHOD = rb_intern("write");
49
+ #endif /* !HAVE_RB_IO_BUFWRITE */
28
50
  }
@@ -4,6 +4,7 @@
4
4
  #define __RXML_IO__
5
5
 
6
6
  int rxml_read_callback(void *context, char *buffer, int len);
7
+ int rxml_write_callback(void *context, const char *buffer, int len);
7
8
  void rxml_init_io(void);
8
9
 
9
10
  #endif
@@ -287,28 +287,67 @@ static VALUE rxml_reader_close(VALUE self)
287
287
 
288
288
  /*
289
289
  * call-seq:
290
- * reader.move_to_attribute(val) -> code
290
+ * reader.move_to_attribute_no(index) -> code
291
291
  *
292
292
  * Move the position of the current instance to the attribute with the
293
- * specified index (if +val+ is an integer) or name (if +val+ is a string)
294
- * relative to the containing element.
293
+ * specified index relative to the containing element.
295
294
  */
296
- static VALUE rxml_reader_move_to_attr(VALUE self, VALUE val)
295
+ static VALUE rxml_reader_move_to_attr_no(VALUE self, VALUE index)
297
296
  {
298
- xmlTextReaderPtr xreader;
299
297
  int ret;
298
+ xmlTextReaderPtr xreader;
300
299
 
301
300
  xreader = rxml_text_reader_get(self);
301
+ ret = xmlTextReaderMoveToAttributeNo(xreader, FIX2INT(index));
302
+
303
+ return INT2FIX(ret);
304
+ }
302
305
 
306
+ /*
307
+ * call-seq:
308
+ * reader.move_to_attribute(localName) -> code
309
+ *
310
+ * Move the position of the current instance to the attribute with the
311
+ * specified name relative to the containing element.
312
+ */
313
+ static VALUE rxml_reader_move_to_attr(VALUE self, VALUE val)
314
+ {
303
315
  if (TYPE(val) == T_FIXNUM)
304
316
  {
305
- ret = xmlTextReaderMoveToAttributeNo(xreader, FIX2INT(val));
317
+ rb_warn("%s::move_to_attribute with a Fixnum argument is deprecated. "
318
+ "Please, consider move_to_attribute_no method instead.",
319
+ rb_class2name(cXMLReader));
320
+ return rxml_reader_move_to_attr_no(self, val);
306
321
  }
307
322
  else
308
323
  {
324
+ int ret;
325
+ xmlTextReaderPtr xreader;
326
+
327
+ xreader = rxml_text_reader_get(self);
309
328
  ret = xmlTextReaderMoveToAttribute(xreader,
310
329
  (const xmlChar *) StringValueCStr(val));
330
+
331
+ return INT2FIX(ret);
311
332
  }
333
+ }
334
+
335
+ /*
336
+ * call-seq:
337
+ * reader.move_to_attribute_ns(localName, namespaceURI) -> code
338
+ *
339
+ * Move the position of the current instance to the attribute with the
340
+ * specified name and namespace relative to the containing element.
341
+ */
342
+ static VALUE rxml_reader_move_to_attr_ns(VALUE self, VALUE name, VALUE ns)
343
+ {
344
+ int ret;
345
+ xmlTextReaderPtr xreader;
346
+
347
+ xreader = rxml_text_reader_get(self);
348
+ ret = xmlTextReaderMoveToAttributeNs(xreader,
349
+ (const xmlChar *) StringValueCStr(name),
350
+ (const xmlChar *) StringValueCStr(ns));
312
351
 
313
352
  return INT2FIX(ret);
314
353
  }
@@ -862,6 +901,71 @@ static VALUE rxml_reader_attribute(VALUE self, VALUE key)
862
901
  return result;
863
902
  }
864
903
 
904
+ /*
905
+ * call-seq:
906
+ * reader.get_attribute(localName) -> value
907
+ *
908
+ * Provide the value of the attribute with the specified name
909
+ * relative to the containing element.
910
+ */
911
+ static VALUE rxml_reader_get_attribute(VALUE self, VALUE name)
912
+ {
913
+ VALUE result = Qnil;
914
+ xmlChar *xattr;
915
+ xmlTextReaderPtr xReader = rxml_text_reader_get(self);
916
+ const xmlChar *xencoding = xmlTextReaderConstEncoding(xReader);
917
+
918
+ xattr = xmlTextReaderGetAttribute(xReader,
919
+ (const xmlChar *) StringValueCStr(name));
920
+ if (xattr)
921
+ {
922
+ result = rxml_new_cstr(xattr, xencoding);
923
+ xmlFree(xattr);
924
+ }
925
+ return result;
926
+ }
927
+
928
+ /*
929
+ * call-seq:
930
+ * reader.get_attribute_no(index) -> value
931
+ *
932
+ * Provide the value of the attribute with the specified index
933
+ * relative to the containing element.
934
+ */
935
+ static VALUE rxml_reader_get_attribute_no(VALUE self, VALUE index)
936
+ {
937
+ VALUE result = Qnil;
938
+ xmlChar *xattr;
939
+ xmlTextReaderPtr xReader = rxml_text_reader_get(self);
940
+ const xmlChar *xencoding = xmlTextReaderConstEncoding(xReader);
941
+
942
+ xattr = xmlTextReaderGetAttributeNo(xReader, FIX2INT(index));
943
+ if (xattr)
944
+ {
945
+ result = rxml_new_cstr(xattr, xencoding);
946
+ xmlFree(xattr);
947
+ }
948
+ return result;
949
+ }
950
+
951
+ static VALUE rxml_reader_get_attribute_ns(VALUE self, VALUE name, VALUE ns)
952
+ {
953
+ VALUE result = Qnil;
954
+ xmlChar *xattr;
955
+ xmlTextReaderPtr xReader = rxml_text_reader_get(self);
956
+ const xmlChar *xencoding = xmlTextReaderConstEncoding(xReader);
957
+
958
+ xattr = xmlTextReaderGetAttributeNs(xReader,
959
+ (const xmlChar *) StringValueCStr(name),
960
+ (const xmlChar *) StringValueCStr(ns));
961
+ if (xattr)
962
+ {
963
+ result = rxml_new_cstr(xattr, xencoding);
964
+ xmlFree(xattr);
965
+ }
966
+ return result;
967
+ }
968
+
865
969
  /*
866
970
  * call-seq:
867
971
  * reader.lookup_namespace(prefix) -> value
@@ -1040,6 +1144,9 @@ void rxml_init_reader(void)
1040
1144
  rb_define_method(cXMLReader, "depth", rxml_reader_depth, 0);
1041
1145
  rb_define_method(cXMLReader, "encoding", rxml_reader_encoding, 0);
1042
1146
  rb_define_method(cXMLReader, "expand", rxml_reader_expand, 0);
1147
+ rb_define_method(cXMLReader, "get_attribute", rxml_reader_get_attribute, 1);
1148
+ rb_define_method(cXMLReader, "get_attribute_no", rxml_reader_get_attribute_no, 1);
1149
+ rb_define_method(cXMLReader, "get_attribute_ns", rxml_reader_get_attribute_ns, 2);
1043
1150
  rb_define_method(cXMLReader, "has_attributes?", rxml_reader_has_attributes, 0);
1044
1151
  rb_define_method(cXMLReader, "has_value?", rxml_reader_has_value, 0);
1045
1152
  #if LIBXML_VERSION >= 20617
@@ -1048,6 +1155,8 @@ void rxml_init_reader(void)
1048
1155
  rb_define_method(cXMLReader, "local_name", rxml_reader_local_name, 0);
1049
1156
  rb_define_method(cXMLReader, "lookup_namespace", rxml_reader_lookup_namespace, 1);
1050
1157
  rb_define_method(cXMLReader, "move_to_attribute", rxml_reader_move_to_attr, 1);
1158
+ rb_define_method(cXMLReader, "move_to_attribute_no", rxml_reader_move_to_attr_no, 1);
1159
+ rb_define_method(cXMLReader, "move_to_attribute_ns", rxml_reader_move_to_attr_ns, 2);
1051
1160
  rb_define_method(cXMLReader, "move_to_first_attribute", rxml_reader_move_to_first_attr, 0);
1052
1161
  rb_define_method(cXMLReader, "move_to_next_attribute", rxml_reader_move_to_next_attr, 0);
1053
1162
  rb_define_method(cXMLReader, "move_to_element", rxml_reader_move_to_element, 0);
@@ -15,7 +15,7 @@
15
15
  * The XML::Schema class is used to prepare XML Schemas for validation of xml
16
16
  * documents.
17
17
  *
18
- * Schemas can be created from XML documents, strings or URIs using the
18
+ * Schemas can be created from XML documents, strinings or URIs using the
19
19
  * corresponding methods (new for URIs).
20
20
  *
21
21
  * Once a schema is prepared, an XML document can be validated by the
@@ -238,6 +238,13 @@ rxml_schema_type_attributes(VALUE self)
238
238
 
239
239
  void rxml_init_schema_type(void)
240
240
  {
241
+ /* Add in infinity support for ruby 1.8.7 */
242
+ #if !defined(RUBY_VM) && defined(INFINITY)
243
+ ID infinityId = rb_intern("INFINITY");
244
+ if (rb_const_defined(rb_cFloat, infinityId) == Qfalse)
245
+ rb_define_const(rb_cFloat, "INFINITY", rb_float_new(INFINITY));
246
+ #endif
247
+
241
248
  cXMLSchemaType = rb_define_class_under(cXMLSchema, "Type", rb_cObject);
242
249
 
243
250
  rb_define_method(cXMLSchemaType, "namespace", rxml_schema_type_namespace, 0);
@@ -1,9 +1,9 @@
1
1
  /* Don't nuke this block! It is used for automatically updating the
2
2
  * versions below. VERSION = string formatting, VERNUM = numbered
3
3
  * version for inline testing: increment both or none at all.*/
4
- #define RUBY_LIBXML_VERSION "2.4.0"
5
- #define RUBY_LIBXML_VERNUM 240
4
+ #define RUBY_LIBXML_VERSION "2.5.0"
5
+ #define RUBY_LIBXML_VERNUM 250
6
6
  #define RUBY_LIBXML_VER_MAJ 2
7
- #define RUBY_LIBXML_VER_MIN 4
7
+ #define RUBY_LIBXML_VER_MIN 5
8
8
  #define RUBY_LIBXML_VER_MIC 0
9
9
  #define RUBY_LIBXML_VER_PATCH 0
@@ -0,0 +1,1072 @@
1
+ #include <libxml/xmlversion.h>
2
+
3
+ #ifdef LIBXML_WRITER_ENABLED
4
+
5
+ /*
6
+ * Document-class: LibXML::XML::Writer
7
+ *
8
+ * The XML::Writer class provides a simpler, alternative way to build a valid
9
+ * XML document from scratch (forward-only) compared to a DOM approach (based
10
+ * on XML::Document class).
11
+ *
12
+ * For a more in depth tutorial, albeit in C, see http://xmlsoft.org/xmlwriter.html
13
+ */
14
+
15
+ # include <libxml/xmlwriter.h>
16
+
17
+ # include "ruby_libxml.h"
18
+ # include "ruby_xml_io.h"
19
+ # include "ruby_xml_document.h"
20
+ # include "ruby_xml_writer.h"
21
+
22
+ VALUE cXMLWriter;
23
+ static VALUE sEncoding, sStandalone;
24
+
25
+ typedef enum {
26
+ RXMLW_OUTPUT_NONE,
27
+ RXMLW_OUTPUT_IO,
28
+ RXMLW_OUTPUT_DOC,
29
+ RXMLW_OUTPUT_STRING
30
+ } rxmlw_output_type;
31
+
32
+ typedef struct {
33
+ VALUE output;
34
+ xmlBufferPtr buffer;
35
+ xmlTextWriterPtr writer;
36
+ rxmlw_output_type output_type;
37
+ } rxml_writer_object;
38
+
39
+ # ifdef HAVE_RUBY_ENCODING_H
40
+
41
+ # define /*VALUE*/ rxml_writer_c_to_ruby_string(/*const xmlChar **/ string, /*long*/ string_len) \
42
+ rb_external_str_new_with_enc(string, string_len, rb_utf8_encoding())
43
+
44
+ # define /*VALUE*/ rxml_writer_ruby_string_to_utf8(/*VALUE*/ string) \
45
+ rb_str_conv_enc(string, rb_enc_get(string), rb_utf8_encoding())
46
+ // rb_str_export_to_enc(string, rb_utf8_encoding())
47
+
48
+ # define /*void*/ rxml_writer_free_utf8_string(/*VALUE*/ orig, /*VALUE*/ utf8) \
49
+ do { \
50
+ if (orig != utf8) { \
51
+ rb_str_free(utf8); \
52
+ } \
53
+ } while (0);
54
+
55
+ # else
56
+
57
+ # define /*VALUE*/ rxml_writer_c_to_ruby_string(/*const xmlChar **/ string, /*long*/ string_len) \
58
+ rb_str_new(string, string_len)
59
+
60
+ # define /*VALUE*/ rxml_writer_ruby_string_to_utf8(/*VALUE*/ string) \
61
+ string
62
+
63
+ # define /*void*/ rxml_writer_free_utf8_string(/*VALUE*/ orig, /*VALUE*/ utf8) \
64
+ /* NOP */
65
+
66
+ # endif /* HAVE_RUBY_ENCODING_H */
67
+
68
+ static void rxml_writer_free(rxml_writer_object *rwo)
69
+ {
70
+ #if 0 /* seems to be done by xmlFreeTextWriter */
71
+ if (NULL != rwo->buffer) {
72
+ xmlBufferFree(rwo->buffer);
73
+ }
74
+ #endif
75
+ xmlFreeTextWriter(rwo->writer);
76
+ xfree(rwo);
77
+ }
78
+
79
+ static void rxml_writer_mark(rxml_writer_object *rwo)
80
+ {
81
+ if (!NIL_P(rwo->output)) {
82
+ rb_gc_mark(rwo->output);
83
+ }
84
+ }
85
+
86
+ static VALUE rxml_writer_wrap(rxml_writer_object *rwo)
87
+ {
88
+ return Data_Wrap_Struct(cXMLWriter, rxml_writer_mark, rxml_writer_free, rwo);
89
+ }
90
+
91
+ static rxml_writer_object *rxml_textwriter_get(VALUE obj)
92
+ {
93
+ rxml_writer_object *rwo;
94
+
95
+ Data_Get_Struct(obj, rxml_writer_object, rwo);
96
+
97
+ return rwo;
98
+ }
99
+
100
+ /* ===== public class methods ===== */
101
+
102
+ /* call-seq:
103
+ * XML::Writer::io(io) -> XML::Writer
104
+ *
105
+ * Creates a XML::Writer which will write XML directly into an IO object.
106
+ */
107
+ static VALUE rxml_writer_io(VALUE klass, VALUE io)
108
+ {
109
+ #if 0
110
+ typedef int (*xmlOutputCloseCallback)(void * context);
111
+ typedef int (*xmlOutputWriteCallback)(void * context, const char * buffer, int len);
112
+
113
+ ssize_t rb_io_bufwrite(VALUE io, const void *buf, size_t size);
114
+
115
+ xmlOutputBufferPtr xmlOutputBufferCreateIO(xmlOutputWriteCallback iowrite, xmlOutputCloseCallback ioclose, void * ioctx, xmlCharEncodingHandlerPtr encoder)
116
+
117
+ xmlCharEncodingHandlerPtr xmlFindCharEncodingHandler(const char * name);
118
+ #endif
119
+ xmlOutputBufferPtr out;
120
+ rxml_writer_object *rwo;
121
+
122
+ rwo = ALLOC(rxml_writer_object);
123
+ rwo->output = io;
124
+ rwo->buffer = NULL;
125
+ rwo->output_type = RXMLW_OUTPUT_IO;
126
+ if (NULL == (out = xmlOutputBufferCreateIO(rxml_write_callback, NULL, (void *) io, NULL))) {
127
+ rxml_raise(&xmlLastError);
128
+ }
129
+ if (NULL == (rwo->writer = xmlNewTextWriter(out))) {
130
+ rxml_raise(&xmlLastError);
131
+ }
132
+
133
+ return rxml_writer_wrap(rwo);
134
+ }
135
+
136
+
137
+ /* call-seq:
138
+ * XML::Writer::file(path) -> XML::Writer
139
+ *
140
+ * Creates a XML::Writer object which will write XML into the file with
141
+ * the given name.
142
+ */
143
+ static VALUE rxml_writer_file(VALUE klass, VALUE filename)
144
+ {
145
+ rxml_writer_object *rwo;
146
+
147
+ rwo = ALLOC(rxml_writer_object);
148
+ rwo->output = Qnil;
149
+ rwo->buffer = NULL;
150
+ rwo->output_type = RXMLW_OUTPUT_NONE;
151
+ if (NULL == (rwo->writer = xmlNewTextWriterFilename(StringValueCStr(filename), 0))) {
152
+ rxml_raise(&xmlLastError);
153
+ }
154
+
155
+ return rxml_writer_wrap(rwo);
156
+ }
157
+
158
+ /* call-seq:
159
+ * XML::Writer::string -> XML::Writer
160
+ *
161
+ * Creates a XML::Writer which will write XML into memory, as string.
162
+ */
163
+ static VALUE rxml_writer_string(VALUE klass)
164
+ {
165
+ rxml_writer_object *rwo;
166
+
167
+ rwo = ALLOC(rxml_writer_object);
168
+ rwo->output = Qnil;
169
+ rwo->output_type = RXMLW_OUTPUT_STRING;
170
+ if (NULL == (rwo->buffer = xmlBufferCreate())) {
171
+ rxml_raise(&xmlLastError);
172
+ }
173
+ if (NULL == (rwo->writer = xmlNewTextWriterMemory(rwo->buffer, 0))) {
174
+ xmlBufferFree(rwo->buffer);
175
+ rxml_raise(&xmlLastError);
176
+ }
177
+
178
+ return rxml_writer_wrap(rwo);
179
+ }
180
+
181
+ /* call-seq:
182
+ * XML::Writer::document -> XML::Writer
183
+ *
184
+ * Creates a XML::Writer which will write into an in memory XML::Document
185
+ */
186
+ static VALUE rxml_writer_doc(VALUE klass)
187
+ {
188
+ xmlDocPtr doc;
189
+ rxml_writer_object *rwo;
190
+
191
+ rwo = ALLOC(rxml_writer_object);
192
+ rwo->buffer = NULL;
193
+ rwo->output = Qnil;
194
+ rwo->output_type = RXMLW_OUTPUT_DOC;
195
+ if (NULL == (rwo->writer = xmlNewTextWriterDoc(&doc, 0))) {
196
+ rxml_raise(&xmlLastError);
197
+ }
198
+ rwo->output = rxml_document_wrap(doc);
199
+
200
+ return rxml_writer_wrap(rwo);
201
+ }
202
+
203
+ /* ===== public instance methods ===== */
204
+
205
+ /* call-seq:
206
+ * writer.flush -> (true|false)
207
+ *
208
+ * Flushes the output buffer.
209
+ */
210
+ static VALUE rxml_writer_flush(VALUE self)
211
+ {
212
+ int ret;
213
+ rxml_writer_object *rwo;
214
+
215
+ rwo = rxml_textwriter_get(self);
216
+ ret = xmlTextWriterFlush(rwo->writer);
217
+
218
+ return (-1 == ret ? Qfalse : Qtrue);
219
+ }
220
+
221
+ /* call-seq:
222
+ * writer.result -> (XML::Document|"string"|nil)
223
+ *
224
+ * Returns the associated result object to the XML::Writer creation.
225
+ * A String for a XML::Writer object created with XML::Writer::string,
226
+ * a XML::Document with XML::Writer::document, etc.
227
+ */
228
+ static VALUE rxml_writer_result(VALUE self)
229
+ {
230
+ VALUE ret;
231
+ rxml_writer_object *rwo;
232
+
233
+ ret = Qnil;
234
+ rwo = rxml_textwriter_get(self);
235
+ switch (rwo->output_type) {
236
+ case RXMLW_OUTPUT_DOC:
237
+ ret = rwo->output;
238
+ break;
239
+ case RXMLW_OUTPUT_STRING:
240
+ ret = rxml_writer_c_to_ruby_string(rwo->buffer->content, rwo->buffer->use);
241
+ break;
242
+ case RXMLW_OUTPUT_IO:
243
+ case RXMLW_OUTPUT_NONE:
244
+ break;
245
+ default:
246
+ rb_bug("unexpected output");
247
+ break;
248
+ }
249
+
250
+ return ret;
251
+ }
252
+
253
+ /* ===== private helpers ===== */
254
+
255
+ static VALUE numeric_rxml_writer_void(VALUE obj, int (*fn)(xmlTextWriterPtr))
256
+ {
257
+ int ret;
258
+ rxml_writer_object *rwo;
259
+
260
+ rwo = rxml_textwriter_get(obj);
261
+ ret = fn(rwo->writer);
262
+
263
+ return (-1 == ret ? Qfalse : Qtrue);
264
+ }
265
+
266
+ # define numeric_rxml_writer_string(/*VALUE*/ obj, /*VALUE*/ name_or_content, /*int (**/fn/*)(xmlTextWriterPtr, const xmlChar *)*/) \
267
+ numeric_rxml_writer_va_strings(obj, Qnil, 1, fn, name_or_content)
268
+
269
+ /**
270
+ * This is quite ugly but thanks to libxml2 coding style, all xmlTextWriter*
271
+ * calls can be redirected to a single function. This can be convenient to:
272
+ * - avoid repeating yourself
273
+ * - convert strings to UTF-8
274
+ * - validate names
275
+ * and so on
276
+ **/
277
+ # define XMLWRITER_MAX_STRING_ARGS 5
278
+ static VALUE numeric_rxml_writer_va_strings(VALUE obj, VALUE pe, size_t strings_count, int (*fn)(ANYARGS), ...)
279
+ {
280
+ va_list ap;
281
+ int argc, ret;
282
+ rxml_writer_object *rwo;
283
+ const xmlChar *argv[XMLWRITER_MAX_STRING_ARGS];
284
+ VALUE utf8[XMLWRITER_MAX_STRING_ARGS], orig[XMLWRITER_MAX_STRING_ARGS];
285
+
286
+ if (strings_count > XMLWRITER_MAX_STRING_ARGS) {
287
+ rb_bug("more arguments than expected");
288
+ }
289
+ ret = -1;
290
+ va_start(ap, fn);
291
+ rwo = rxml_textwriter_get(obj);
292
+ for (argc = 0; argc < strings_count; argc++) {
293
+ VALUE arg;
294
+
295
+ arg = va_arg(ap, VALUE);
296
+ orig[argc] = arg;
297
+ if (NIL_P(arg)) {
298
+ utf8[argc] = Qnil;
299
+ argv[argc] = NULL;
300
+ } else {
301
+ utf8[argc] = rxml_writer_ruby_string_to_utf8(orig[argc]);
302
+ argv[argc] = BAD_CAST StringValueCStr(utf8[argc]);
303
+ }
304
+ }
305
+ va_end(ap);
306
+
307
+ if (NIL_P(pe)) {
308
+ switch (strings_count) {
309
+ case 0:
310
+ ret = fn(rwo->writer);
311
+ break;
312
+ case 1:
313
+ ret = fn(rwo->writer, argv[0]);
314
+ break;
315
+ case 2:
316
+ ret = fn(rwo->writer, argv[0], argv[1]);
317
+ break;
318
+ case 3:
319
+ ret = fn(rwo->writer, argv[0], argv[1], argv[2]);
320
+ break;
321
+ case 4:
322
+ ret = fn(rwo->writer, argv[0], argv[1], argv[2], argv[3]);
323
+ break;
324
+ case 5:
325
+ ret = fn(rwo->writer, argv[0], argv[1], argv[2], argv[3], argv[4]);
326
+ break;
327
+ default:
328
+ break;
329
+ }
330
+ } else {
331
+ int xpe;
332
+
333
+ xpe = RTEST(pe);
334
+ switch (strings_count) { /* strings_count doesn't include pe */
335
+ case 0:
336
+ ret = fn(rwo->writer, xpe);
337
+ break;
338
+ case 1:
339
+ ret = fn(rwo->writer, xpe, argv[0]);
340
+ break;
341
+ case 2:
342
+ ret = fn(rwo->writer, xpe, argv[0], argv[1]);
343
+ break;
344
+ case 3:
345
+ ret = fn(rwo->writer, xpe, argv[0], argv[1], argv[2]);
346
+ break;
347
+ case 4:
348
+ ret = fn(rwo->writer, xpe, argv[0], argv[1], argv[2], argv[3]);
349
+ break;
350
+ case 5:
351
+ ret = fn(rwo->writer, xpe, argv[0], argv[1], argv[2], argv[3], argv[4]);
352
+ break;
353
+ default:
354
+ break;
355
+ }
356
+ }
357
+ # ifdef HAVE_RUBY_ENCODING_H
358
+ while (--strings_count > 0) {
359
+ if (!NIL_P(orig[strings_count])) {
360
+ rxml_writer_free_utf8_string(orig[strings_count], utf8[strings_count]);
361
+ }
362
+ }
363
+ # endif /* HAVE_RUBY_ENCODING_H */
364
+
365
+ return (-1 == ret ? Qfalse : Qtrue);
366
+ }
367
+
368
+ /* ===== public instance methods ===== */
369
+
370
+ # if LIBXML_VERSION >= 20605
371
+ /* call-seq:
372
+ * writer.set_indent(indentation) -> (true|false)
373
+ *
374
+ * Toggles indentation on or off. Returns +false+ on failure.
375
+ *
376
+ * Availability: libxml2 >= 2.6.5
377
+ */
378
+ static VALUE rxml_writer_set_indent(VALUE self, VALUE indentation)
379
+ {
380
+ int ret;
381
+ rxml_writer_object *rwo;
382
+
383
+ rwo = rxml_textwriter_get(self);
384
+ ret = xmlTextWriterSetIndent(rwo->writer, RTEST(indentation));
385
+
386
+ return (-1 == ret ? Qfalse : Qtrue);
387
+ }
388
+
389
+ /* call-seq:
390
+ * writer.set_indent_string(string) -> (true|false)
391
+ *
392
+ * Sets the string to use to indent each element of the document.
393
+ * Don't forget to enable indentation with set_indent. Returns
394
+ * +false+ on failure.
395
+ *
396
+ * Availability: libxml2 >= 2.6.5
397
+ */
398
+ static VALUE rxml_writer_set_indent_string(VALUE self, VALUE indentation)
399
+ {
400
+ return numeric_rxml_writer_string(self, indentation, xmlTextWriterSetIndentString);
401
+ }
402
+ # endif /* LIBXML_VERSION >= 20605 */
403
+
404
+ /* ===== public full tag interface ===== */
405
+
406
+ /* write_<X> = start_<X> + write_string + end_<X> */
407
+
408
+ /* call-seq:
409
+ * writer.write_comment(content) -> (true|false)
410
+ *
411
+ * Writes a full comment tag, all at once. Returns +false+ on failure.
412
+ * This is equivalent to start_comment + write_string(content) + end_comment.
413
+ */
414
+ static VALUE rxml_writer_write_comment(VALUE self, VALUE content)
415
+ {
416
+ return numeric_rxml_writer_string(self, content, xmlTextWriterWriteComment);
417
+ }
418
+
419
+ /* call-seq:
420
+ * writer.write_cdata(content) -> (true|false)
421
+ *
422
+ * Writes a full CDATA section, all at once. Returns +false+ on failure.
423
+ * This is equivalent to start_cdata + write_string(content) + end_cdata.
424
+ */
425
+ static VALUE rxml_writer_write_cdata(VALUE self, VALUE content)
426
+ {
427
+ return numeric_rxml_writer_string(self, content, xmlTextWriterWriteCDATA);
428
+ }
429
+
430
+ static VALUE rxml_writer_start_element(VALUE, VALUE);
431
+ static VALUE rxml_writer_start_element_ns(int, VALUE *, VALUE);
432
+ static VALUE rxml_writer_end_element(VALUE);
433
+
434
+ /* call-seq:
435
+ * writer.write_element(name, content) -> (true|false)
436
+ *
437
+ * Writes a full element tag, all at once. Returns +false+ on failure.
438
+ * This is equivalent to start_element(name) + write_string(content) +
439
+ * end_element.
440
+ */
441
+ static VALUE rxml_writer_write_element(int argc, VALUE *argv, VALUE self)
442
+ {
443
+ VALUE name, content;
444
+
445
+ rb_scan_args(argc, argv, "11", &name, &content);
446
+ if (Qnil == content) {
447
+ if (Qfalse == rxml_writer_start_element(self, name)) {
448
+ return Qfalse;
449
+ }
450
+ return rxml_writer_end_element(self);
451
+ } else {
452
+ return numeric_rxml_writer_va_strings(self, Qnil, 2, xmlTextWriterWriteElement, name, content);
453
+ }
454
+ }
455
+
456
+ # define ARRAY_SIZE(array) \
457
+ (sizeof(array) / sizeof((array)[0]))
458
+
459
+ /* call-seq:
460
+ * writer.write_element_ns(prefix, name, namespaceURI, content) -> (true|false)
461
+ *
462
+ * Writes a full namespaced element tag, all at once. Returns +false+ on failure.
463
+ * This is a shortcut for start_element_ns(prefix, name, namespaceURI) +
464
+ * write_string(content) + end_element.
465
+ *
466
+ * Notes:
467
+ * - by default, the xmlns: definition is repeated on every element. If you want
468
+ * the prefix, but don't want the xmlns: declaration repeated, set +namespaceURI+
469
+ * to nil or omit it. Don't forget to declare the namespace prefix somewhere
470
+ * earlier.
471
+ * - +content+ can be omitted for an empty tag
472
+ */
473
+ static VALUE rxml_writer_write_element_ns(int argc, VALUE *argv, VALUE self)
474
+ {
475
+ VALUE prefix, name, namespaceURI, content;
476
+
477
+ rb_scan_args(argc, argv, "22", &prefix, &name, &namespaceURI, &content);
478
+ if (Qnil == content) {
479
+ VALUE argv[3] = { prefix, name, namespaceURI };
480
+
481
+ if (Qfalse == rxml_writer_start_element_ns(ARRAY_SIZE(argv), argv, self)) {
482
+ return Qfalse;
483
+ }
484
+ return rxml_writer_end_element(self);
485
+ } else {
486
+ return numeric_rxml_writer_va_strings(self, Qnil, 4, xmlTextWriterWriteElementNS, prefix, name, namespaceURI, content);
487
+ }
488
+ }
489
+
490
+ /* call-seq:
491
+ * writer.write_attribute(name, content) -> (true|false)
492
+ *
493
+ * Writes a full attribute, all at once. Returns +false+ on failure.
494
+ * Same as start_attribute(name) + write_string(content) + end_attribute.
495
+ */
496
+ static VALUE rxml_writer_write_attribute(VALUE self, VALUE name, VALUE content)
497
+ {
498
+ return numeric_rxml_writer_va_strings(self, Qnil, 2, xmlTextWriterWriteAttribute, name, content);
499
+ }
500
+
501
+ /* call-seq:
502
+ * writer.write_attribute_ns(prefix, name, namespaceURI, content) -> (true|false)
503
+ *
504
+ * Writes a full namespaced attribute, all at once. Returns +false+ on failure.
505
+ * Same as start_attribute_ns(prefix, name, namespaceURI) +
506
+ * write_string(content) + end_attribute.
507
+ *
508
+ * Notes:
509
+ * - by default, the xmlns: definition is repeated on every element. If you want
510
+ * the prefix, but don't want the xmlns: declaration repeated, set +namespaceURI+
511
+ * to nil or omit it. Don't forget to declare the namespace prefix somewhere
512
+ * earlier.
513
+ * - +content+ can be omitted too for an empty attribute
514
+ */
515
+ static VALUE rxml_writer_write_attribute_ns(int argc, VALUE *argv, VALUE self)
516
+ {
517
+ VALUE prefix, name, namespaceURI, content;
518
+
519
+ rb_scan_args(argc, argv, "22", &prefix, &name, &namespaceURI, &content);
520
+
521
+ return numeric_rxml_writer_va_strings(self, Qnil, 4, xmlTextWriterWriteAttributeNS, prefix, name, namespaceURI, content);
522
+ }
523
+
524
+ /* call-seq:
525
+ * writer.write_pi(target, content) -> (true|false)
526
+ *
527
+ * Writes a full CDATA tag, all at once. Returns +false+ on failure.
528
+ * This is a shortcut for start_pi(target) + write_string(content) + end_pi.
529
+ */
530
+ static VALUE rxml_writer_write_pi(VALUE self, VALUE target, VALUE content)
531
+ {
532
+ return numeric_rxml_writer_va_strings(self, Qnil, 2, xmlTextWriterWritePI, target, content);
533
+ }
534
+
535
+ /* ===== public start/end interface ===== */
536
+
537
+ /* call-seq:
538
+ * writer.write_string(content) -> (true|false)
539
+ *
540
+ * Safely (problematic characters are internally translated to their
541
+ * associated named entities) writes a string into the current node
542
+ * (attribute, element, comment, ...). Returns +false+ on failure.
543
+ */
544
+ static VALUE rxml_writer_write_string(VALUE self, VALUE content)
545
+ {
546
+ return numeric_rxml_writer_string(self, content, xmlTextWriterWriteString);
547
+ }
548
+
549
+ /* call-seq:
550
+ * writer.write_raw(content) -> (true|false)
551
+ *
552
+ * Writes the string +content+ as is, reserved characters are not
553
+ * translated to their associated entities. Returns +false+ on failure.
554
+ * Consider write_string to handle them.
555
+ */
556
+ static VALUE rxml_writer_write_raw(VALUE self, VALUE content)
557
+ {
558
+ return numeric_rxml_writer_string(self, content, xmlTextWriterWriteRaw);
559
+ }
560
+
561
+ /* call-seq:
562
+ * writer.start_attribute(name) -> (true|false)
563
+ *
564
+ * Starts an attribute. Returns +false+ on failure.
565
+ */
566
+ static VALUE rxml_writer_start_attribute(VALUE self, VALUE name)
567
+ {
568
+ return numeric_rxml_writer_string(self, name, xmlTextWriterStartAttribute);
569
+ }
570
+
571
+ /* call-seq:
572
+ * writer.start_attribute_ns(prefix, name, namespaceURI) -> (true|false)
573
+ *
574
+ * Starts a namespaced attribute. Returns +false+ on failure.
575
+ *
576
+ * Note: by default, the xmlns: definition is repeated on every element. If
577
+ * you want the prefix, but don't want the xmlns: declaration repeated, set
578
+ * +namespaceURI+ to nil or omit it. Don't forget to declare the namespace
579
+ * prefix somewhere earlier.
580
+ */
581
+ static VALUE rxml_writer_start_attribute_ns(int argc, VALUE *argv, VALUE self)
582
+ {
583
+ VALUE prefix, name, namespaceURI;
584
+
585
+ rb_scan_args(argc, argv, "21", &prefix, &name, &namespaceURI);
586
+
587
+ return numeric_rxml_writer_va_strings(self, Qnil, 3, xmlTextWriterStartAttributeNS, prefix, name, namespaceURI);
588
+ }
589
+
590
+ /* call-seq:
591
+ * writer.end_attribute -> (true|false)
592
+ *
593
+ * Ends an attribute, namespaced or not. Returns +false+ on failure.
594
+ */
595
+ static VALUE rxml_writer_end_attribute(VALUE self)
596
+ {
597
+ return numeric_rxml_writer_void(self, xmlTextWriterEndAttribute);
598
+ }
599
+
600
+ # if LIBXML_VERSION >= 20607
601
+ /* call-seq:
602
+ * writer.start_comment -> (true|false)
603
+ *
604
+ * Starts a comment. Returns +false+ on failure.
605
+ * Note: libxml2 >= 2.6.7 required
606
+ */
607
+ static VALUE rxml_writer_start_comment(VALUE self)
608
+ {
609
+ return numeric_rxml_writer_void(self, xmlTextWriterStartComment);
610
+ }
611
+
612
+ /* call-seq:
613
+ * writer.end_comment -> (true|false)
614
+ *
615
+ * Ends current comment, returns +false+ on failure.
616
+ * Note: libxml2 >= 2.6.7 required
617
+ */
618
+ static VALUE rxml_writer_end_comment(VALUE self)
619
+ {
620
+ return numeric_rxml_writer_void(self, xmlTextWriterEndComment);
621
+ }
622
+ # endif /* LIBXML_VERSION >= 20607 */
623
+
624
+ /* call-seq:
625
+ * writer.start_element(name) -> (true|false)
626
+ *
627
+ * Starts a new element. Returns +false+ on failure.
628
+ */
629
+ static VALUE rxml_writer_start_element(VALUE self, VALUE name)
630
+ {
631
+ return numeric_rxml_writer_string(self, name, xmlTextWriterStartElement);
632
+ }
633
+
634
+ /* call-seq:
635
+ * writer.start_element_ns(prefix, name, namespaceURI) -> (true|false)
636
+ *
637
+ * Starts a new namespaced element. Returns +false+ on failure.
638
+ *
639
+ * Note: by default, the xmlns: definition is repeated on every element. If
640
+ * you want the prefix, but don't want the xmlns: declaration repeated, set
641
+ * +namespaceURI+ to nil or omit it. Don't forget to declare the namespace
642
+ * prefix somewhere earlier.
643
+ */
644
+ static VALUE rxml_writer_start_element_ns(int argc, VALUE *argv, VALUE self)
645
+ {
646
+ VALUE prefix, name, namespaceURI;
647
+
648
+ rb_scan_args(argc, argv, "21", &prefix, &name, &namespaceURI);
649
+
650
+ return numeric_rxml_writer_va_strings(self, Qnil, 3, xmlTextWriterStartElementNS, prefix, name, namespaceURI);
651
+ }
652
+
653
+ /* call-seq:
654
+ * writer.end_element -> (true|false)
655
+ *
656
+ * Ends current element, namespaced or not. Returns +false+ on failure.
657
+ */
658
+ static VALUE rxml_writer_end_element(VALUE self)
659
+ {
660
+ return numeric_rxml_writer_void(self, xmlTextWriterEndElement);
661
+ }
662
+
663
+ /* call-seq:
664
+ * writer.write_full_end_element -> (true|false)
665
+ *
666
+ * Ends current element, namespaced or not. Returns +false+ on failure.
667
+ * This method writes an end tag even if the element is empty (<foo></foo>),
668
+ * end_element does not (<foo/>).
669
+ */
670
+ static VALUE rxml_writer_full_end_element(VALUE self)
671
+ {
672
+ return numeric_rxml_writer_void(self, xmlTextWriterFullEndElement);
673
+ }
674
+
675
+ /* call-seq:
676
+ * writer.start_cdata -> (true|false)
677
+ *
678
+ * Starts a new CDATA section. Returns +false+ on failure.
679
+ */
680
+ static VALUE rxml_writer_start_cdata(VALUE self)
681
+ {
682
+ return numeric_rxml_writer_void(self, xmlTextWriterStartCDATA);
683
+ }
684
+
685
+ /* call-seq:
686
+ * writer.end_cdata -> (true|false)
687
+ *
688
+ * Ends current CDATA section. Returns +false+ on failure.
689
+ */
690
+ static VALUE rxml_writer_end_cdata(VALUE self)
691
+ {
692
+ return numeric_rxml_writer_void(self, xmlTextWriterEndCDATA);
693
+ }
694
+
695
+ /* call-seq:
696
+ * writer.start_document -> (true|false)
697
+ * writer.start_document(:encoding => XML::Encoding::UTF_8,
698
+ * :standalone => true) -> (true|false)
699
+ *
700
+ * Starts a new document. Returns +false+ on failure.
701
+ *
702
+ * You may provide an optional hash table to control XML header that will be
703
+ * generated. Valid options are:
704
+ * - encoding: the output document encoding, defaults to nil (= UTF-8). Valid
705
+ * values are the encoding constants defined on XML::Encoding
706
+ * - standalone: nil (default) or a boolean to indicate if the document is
707
+ * standalone or not
708
+ */
709
+ static VALUE rxml_writer_start_document(int argc, VALUE *argv, VALUE self)
710
+ {
711
+ int ret;
712
+ VALUE options;
713
+ rxml_writer_object *rwo;
714
+ const char *xencoding, *xstandalone;
715
+
716
+ options = Qnil;
717
+ xstandalone = xencoding = NULL;
718
+ rb_scan_args(argc, argv, "01", &options);
719
+ if (!NIL_P(options)) {
720
+ VALUE encoding, standalone;
721
+
722
+ encoding = standalone = Qnil;
723
+ Check_Type(options, T_HASH);
724
+ encoding = rb_hash_aref(options, sEncoding);
725
+ xencoding = NIL_P(encoding) ? NULL : xmlGetCharEncodingName(NUM2INT(encoding));
726
+ standalone = rb_hash_aref(options, sStandalone);
727
+ if (NIL_P(standalone)) {
728
+ xstandalone = NULL;
729
+ } else {
730
+ xstandalone = RTEST(standalone) ? "yes" : "no";
731
+ }
732
+ }
733
+ rwo = rxml_textwriter_get(self);
734
+ ret = xmlTextWriterStartDocument(rwo->writer, NULL, xencoding, xstandalone);
735
+
736
+ return (-1 == ret ? Qfalse : Qtrue);
737
+ }
738
+
739
+ /* call-seq:
740
+ * writer.end_document -> (true|false)
741
+ *
742
+ * Ends current document. Returns +false+ on failure.
743
+ */
744
+ static VALUE rxml_writer_end_document(VALUE self)
745
+ {
746
+ return numeric_rxml_writer_void(self, xmlTextWriterEndDocument);
747
+ }
748
+
749
+ /* call-seq:
750
+ * writer.start_pi(target) -> (true|false)
751
+ *
752
+ * Starts a new processing instruction. Returns +false+ on failure.
753
+ */
754
+ static VALUE rxml_writer_start_pi(VALUE self, VALUE target)
755
+ {
756
+ return numeric_rxml_writer_string(self, target, xmlTextWriterStartPI);
757
+ }
758
+
759
+ /* call-seq:
760
+ * writer.end_pi -> (true|false)
761
+ *
762
+ * Ends current processing instruction. Returns +false+ on failure.
763
+ */
764
+ static VALUE rxml_writer_end_pi(VALUE self)
765
+ {
766
+ return numeric_rxml_writer_void(self, xmlTextWriterEndPI);
767
+ }
768
+
769
+ /* call-seq:
770
+ * writer.start_dtd(qualifiedName, publicId, systemId) -> (true|false)
771
+ *
772
+ * Starts a DTD. Returns +false+ on failure.
773
+ */
774
+ static VALUE rxml_writer_start_dtd(int argc, VALUE *argv, VALUE self)
775
+ {
776
+ VALUE name, pubid, sysid;
777
+
778
+ rb_scan_args(argc, argv, "12", &name, &pubid, &sysid);
779
+
780
+ return numeric_rxml_writer_va_strings(self, Qnil, 3, xmlTextWriterStartDTD, name, pubid, sysid);
781
+ }
782
+
783
+ /* call-seq:
784
+ * writer.start_dtd_element(qualifiedName) -> (true|false)
785
+ *
786
+ * Starts a DTD element (<!ELEMENT ... >). Returns +false+ on failure.
787
+ */
788
+ static VALUE rxml_writer_start_dtd_element(VALUE self, VALUE name)
789
+ {
790
+ return numeric_rxml_writer_string(self, name, xmlTextWriterStartDTDElement);
791
+ }
792
+
793
+ /* call-seq:
794
+ * writer.start_dtd_entity(name, pe = false) -> (true|false)
795
+ *
796
+ * Starts a DTD entity (<!ENTITY ... >). Returns +false+ on failure.
797
+ */
798
+ static VALUE rxml_writer_start_dtd_entity(int argc, VALUE *argv, VALUE self)
799
+ {
800
+ VALUE name, pe;
801
+
802
+ rb_scan_args(argc, argv, "11", &name, &pe);
803
+ if (NIL_P(pe)) {
804
+ pe = Qfalse;
805
+ }
806
+
807
+ return numeric_rxml_writer_va_strings(self, pe, 1, xmlTextWriterStartDTDEntity, name);
808
+ }
809
+
810
+ /* call-seq:
811
+ * writer.start_dtd_attlist(name) -> (true|false)
812
+ *
813
+ * Starts a DTD attribute list (<!ATTLIST ... >). Returns +false+ on failure.
814
+ */
815
+ static VALUE rxml_writer_start_dtd_attlist(VALUE self, VALUE name)
816
+ {
817
+ return numeric_rxml_writer_string(self, name, xmlTextWriterStartDTDAttlist);
818
+ }
819
+
820
+ /* call-seq:
821
+ * writer.end_dtd -> (true|false)
822
+ *
823
+ * Ends current DTD, returns +false+ on failure.
824
+ */
825
+ static VALUE rxml_writer_end_dtd(VALUE self)
826
+ {
827
+ return numeric_rxml_writer_void(self, xmlTextWriterEndDTD);
828
+ }
829
+
830
+ /* call-seq:
831
+ * writer.end_dtd_entity -> (true|false)
832
+ *
833
+ * Ends current DTD entity, returns +false+ on failure.
834
+ */
835
+ static VALUE rxml_writer_end_dtd_entity(VALUE self)
836
+ {
837
+ return numeric_rxml_writer_void(self, xmlTextWriterEndDTDEntity);
838
+ }
839
+
840
+ /* call-seq:
841
+ * writer.end_dtd_attlist -> (true|false)
842
+ *
843
+ * Ends current DTD attribute list, returns +false+ on failure.
844
+ */
845
+ static VALUE rxml_writer_end_dtd_attlist(VALUE self)
846
+ {
847
+ return numeric_rxml_writer_void(self, xmlTextWriterEndDTDAttlist);
848
+ }
849
+
850
+ /* call-seq:
851
+ * writer.end_dtd_element -> (true|false)
852
+ *
853
+ * Ends current DTD element, returns +false+ on failure.
854
+ */
855
+ static VALUE rxml_writer_end_dtd_element(VALUE self)
856
+ {
857
+ return numeric_rxml_writer_void(self, xmlTextWriterEndDTDElement);
858
+ }
859
+
860
+ /* call-seq:
861
+ * writer.write_dtd(name [ [ [, publicId ], systemId ], subset ]) -> (true|false)
862
+ *
863
+ * Writes a DTD, all at once. Returns +false+ on failure.
864
+ * - name: dtd name
865
+ * - publicId: external subset public identifier, use nil for a SYSTEM doctype
866
+ * - systemId: external subset system identifier
867
+ * - subset: content
868
+ *
869
+ * Examples:
870
+ * writer.write_dtd 'html'
871
+ * # => <!DOCTYPE html>
872
+ * writer.write_dtd 'docbook', nil, 'http://www.docbook.org/xml/5.0/dtd/docbook.dtd'
873
+ * # => <!DOCTYPE docbook SYSTEM "http://www.docbook.org/xml/5.0/dtd/docbook.dtd">
874
+ * writer.write_dtd 'html', '-//W3C//DTD XHTML 1.1//EN', 'http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd'
875
+ * # => <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
876
+ * writer.write_dtd 'person', nil, nil, '<!ELEMENT person (firstname,lastname)><!ELEMENT firstname (#PCDATA)><!ELEMENT lastname (#PCDATA)>'
877
+ * # => <!DOCTYPE person [<!ELEMENT person (firstname,lastname)><!ELEMENT firstname (#PCDATA)><!ELEMENT lastname (#PCDATA)>]>
878
+ */
879
+ static VALUE rxml_writer_write_dtd(int argc, VALUE *argv, VALUE self)
880
+ {
881
+ VALUE name, pubid, sysid, subset;
882
+
883
+ rb_scan_args(argc, argv, "13", &name, &pubid, &sysid, &subset);
884
+
885
+ return numeric_rxml_writer_va_strings(self, Qnil, 4, xmlTextWriterWriteDTD, name, pubid, sysid, subset);
886
+ }
887
+
888
+ /* call-seq:
889
+ * writer.write_dtd_attlist(name, content) -> (true|false)
890
+ *
891
+ * Writes a DTD attribute list, all at once. Returns +false+ on failure.
892
+ * writer.write_dtd_attlist 'id', 'ID #IMPLIED'
893
+ * # => <!ATTLIST id ID #IMPLIED>
894
+ */
895
+ static VALUE rxml_writer_write_dtd_attlist(VALUE self, VALUE name, VALUE content)
896
+ {
897
+ return numeric_rxml_writer_va_strings(self, Qnil, 2, xmlTextWriterWriteDTDAttlist, name, content);
898
+ }
899
+
900
+ /* call-seq:
901
+ * writer.write_dtd_element(name, content) -> (true|false)
902
+ *
903
+ * Writes a full DTD element, all at once. Returns +false+ on failure.
904
+ * writer.write_dtd_element 'person', '(firstname,lastname)'
905
+ * # => <!ELEMENT person (firstname,lastname)>
906
+ */
907
+ static VALUE rxml_writer_write_dtd_element(VALUE self, VALUE name, VALUE content)
908
+ {
909
+ return numeric_rxml_writer_va_strings(self, Qnil, 2, xmlTextWriterWriteDTDElement, name, content);
910
+ }
911
+
912
+ /* call-seq:
913
+ * writer.write_dtd_entity(name, publicId, systemId, ndataid, content, pe) -> (true|false)
914
+ *
915
+ * Writes a DTD entity, all at once. Returns +false+ on failure.
916
+ */
917
+ static VALUE rxml_writer_write_dtd_entity(VALUE self, VALUE name, VALUE pubid, VALUE sysid, VALUE ndataid, VALUE content, VALUE pe)
918
+ {
919
+ return numeric_rxml_writer_va_strings(self, pe, 5, xmlTextWriterWriteDTDEntity, name, pubid, sysid, ndataid, content);
920
+ }
921
+
922
+ /* call-seq:
923
+ * writer.write_dtd_external_entity(name, publicId, systemId, ndataid, pe) -> (true|false)
924
+ *
925
+ * Writes a DTD external entity. The entity must have been started
926
+ * with start_dtd_entity. Returns +false+ on failure.
927
+ * - name: the name of the DTD entity
928
+ * - publicId: the public identifier, which is an alternative to the system identifier
929
+ * - systemId: the system identifier, which is the URI of the DTD
930
+ * - ndataid: the xml notation name
931
+ * - pe: +true+ if this is a parameter entity (to be used only in the DTD
932
+ * itself), +false+ if not
933
+ */
934
+ static VALUE rxml_writer_write_dtd_external_entity(VALUE self, VALUE name, VALUE pubid, VALUE sysid, VALUE ndataid, VALUE pe)
935
+ {
936
+ return numeric_rxml_writer_va_strings(self, pe, 4, xmlTextWriterWriteDTDExternalEntity, name, pubid, sysid, ndataid);
937
+ }
938
+
939
+ /* call-seq:
940
+ * writer.write_dtd_external_entity_contents(publicId, systemId, ndataid) -> (true|false)
941
+ *
942
+ * Writes the contents of a DTD external entity, all at once. Returns +false+ on failure.
943
+ */
944
+ static VALUE rxml_writer_write_dtd_external_entity_contents(VALUE self, VALUE pubid, VALUE sysid, VALUE ndataid)
945
+ {
946
+ return numeric_rxml_writer_va_strings(self, Qnil, 3, xmlTextWriterWriteDTDExternalEntityContents, pubid, sysid, ndataid);
947
+ }
948
+
949
+ /* call-seq:
950
+ * writer.write_dtd_internal_entity(name, content, pe) -> (true|false)
951
+ *
952
+ * Writes a DTD internal entity, all at once. Returns +false+ on failure.
953
+ *
954
+ * Examples:
955
+ * writer.write_dtd_entity 'Shape', '(rect|circle|poly|default)', true
956
+ * # => <!ENTITY % Shape "(rect|circle|poly|default)">
957
+ * writer.write_dtd_entity 'delta', '&#948;', false
958
+ * # => <!ENTITY delta "&#948;">
959
+ */
960
+ static VALUE rxml_writer_write_dtd_internal_entity(VALUE self, VALUE name, VALUE content, VALUE pe)
961
+ {
962
+ return numeric_rxml_writer_va_strings(self, pe, 2, xmlTextWriterWriteDTDInternalEntity, name, content);
963
+ }
964
+
965
+ /* call-seq:
966
+ * writer.write_dtd_notation(name, publicId, systemId) -> (true|false)
967
+ *
968
+ * Writes a DTD entity, all at once. Returns +false+ on failure.
969
+ */
970
+ static VALUE rxml_writer_write_dtd_notation(VALUE self, VALUE name, VALUE pubid, VALUE sysid)
971
+ {
972
+ return numeric_rxml_writer_va_strings(self, Qnil, 3, xmlTextWriterWriteDTDNotation, name, pubid, sysid);
973
+ }
974
+
975
+ # if LIBXML_VERSION >= 20900
976
+ /* call-seq:
977
+ * writer.set_quote_char(...) -> (true|false)
978
+ *
979
+ * Sets the character used to quote attributes. Returns +false+ on failure.
980
+ *
981
+ * Notes:
982
+ * - only " (default) and ' characters are valid
983
+ * - availability: libxml2 >= 2.9.0
984
+ */
985
+ static VALUE rxml_writer_set_quote_char(VALUE self, VALUE quotechar)
986
+ {
987
+ int ret;
988
+ const char *xquotechar;
989
+ rxml_writer_object *rwo;
990
+
991
+ rwo = rxml_textwriter_get(self);
992
+ xquotechar = StringValueCStr(quotechar);
993
+ ret = xmlTextWriterSetQuoteChar(rwo->writer, (xmlChar) xquotechar[0]);
994
+
995
+ return (-1 == ret ? Qfalse : Qtrue);
996
+ }
997
+ # endif /* LIBXML_VERSION >= 20900 */
998
+
999
+ /* grep -P 'xmlTextWriter(Start|End|Write)(?!DTD|V?Format)[^(]+' /usr/include/libxml2/libxml/xmlwriter.h */
1000
+ void rxml_init_writer(void)
1001
+ {
1002
+ sEncoding = ID2SYM(rb_intern("encoding"));
1003
+ sStandalone = ID2SYM(rb_intern("standalone"));
1004
+
1005
+ cXMLWriter = rb_define_class_under(mXML, "Writer", rb_cObject);
1006
+
1007
+ rb_define_singleton_method(cXMLWriter, "io", rxml_writer_io, 1);
1008
+ rb_define_singleton_method(cXMLWriter, "file", rxml_writer_file, 1);
1009
+ rb_define_singleton_method(cXMLWriter, "document", rxml_writer_doc, 0);
1010
+ rb_define_singleton_method(cXMLWriter, "string", rxml_writer_string, 0);
1011
+
1012
+ /* misc */
1013
+ # if LIBXML_VERSION >= 20605
1014
+ rb_define_method(cXMLWriter, "set_indent", rxml_writer_set_indent, 1);
1015
+ rb_define_method(cXMLWriter, "set_indent_string", rxml_writer_set_indent_string, 1);
1016
+ # endif /* LIBXML_VERSION >= 20605 */
1017
+ # if LIBXML_VERSION >= 20900
1018
+ rb_define_method(cXMLWriter, "set_quote_char", rxml_writer_set_quote_char, 1);
1019
+ # endif /* LIBXML_VERSION >= 20900 */
1020
+ rb_define_method(cXMLWriter, "flush", rxml_writer_flush, 0);
1021
+ rb_define_method(cXMLWriter, "start_dtd", rxml_writer_start_dtd, -1);
1022
+ rb_define_method(cXMLWriter, "start_dtd_entity", rxml_writer_start_dtd_entity, -1);
1023
+ rb_define_method(cXMLWriter, "start_dtd_attlist", rxml_writer_start_dtd_attlist, 1);
1024
+ rb_define_method(cXMLWriter, "start_dtd_element", rxml_writer_start_dtd_element, 1);
1025
+ rb_define_method(cXMLWriter, "write_dtd", rxml_writer_write_dtd, -1);
1026
+ rb_define_method(cXMLWriter, "write_dtd_attlist", rxml_writer_write_dtd_attlist, 2);
1027
+ rb_define_method(cXMLWriter, "write_dtd_element", rxml_writer_write_dtd_element, 2);
1028
+ rb_define_method(cXMLWriter, "write_dtd_entity", rxml_writer_write_dtd_entity, 6);
1029
+ rb_define_method(cXMLWriter, "write_dtd_external_entity", rxml_writer_write_dtd_external_entity, 5);
1030
+ rb_define_method(cXMLWriter, "write_dtd_external_entity_contents", rxml_writer_write_dtd_external_entity_contents, 3);
1031
+ rb_define_method(cXMLWriter, "write_dtd_internal_entity", rxml_writer_write_dtd_internal_entity, 3);
1032
+ rb_define_method(cXMLWriter, "write_dtd_notation", rxml_writer_write_dtd_notation, 3);
1033
+ rb_define_method(cXMLWriter, "end_dtd", rxml_writer_end_dtd, 0);
1034
+ rb_define_method(cXMLWriter, "end_dtd_entity", rxml_writer_end_dtd_entity, 0);
1035
+ rb_define_method(cXMLWriter, "end_dtd_attlist", rxml_writer_end_dtd_attlist, 0);
1036
+ rb_define_method(cXMLWriter, "end_dtd_element", rxml_writer_end_dtd_element, 0);
1037
+
1038
+ /* tag by parts */
1039
+ rb_define_method(cXMLWriter, "write_raw", rxml_writer_write_raw, 1);
1040
+ rb_define_method(cXMLWriter, "write_string", rxml_writer_write_string, 1);
1041
+
1042
+ rb_define_method(cXMLWriter, "start_cdata", rxml_writer_start_cdata, 0);
1043
+ rb_define_method(cXMLWriter, "end_cdata", rxml_writer_end_cdata, 0);
1044
+ rb_define_method(cXMLWriter, "start_attribute", rxml_writer_start_attribute, 1);
1045
+ rb_define_method(cXMLWriter, "start_attribute_ns", rxml_writer_start_attribute_ns, -1);
1046
+ rb_define_method(cXMLWriter, "end_attribute", rxml_writer_end_attribute, 0);
1047
+ rb_define_method(cXMLWriter, "start_element", rxml_writer_start_element, 1);
1048
+ rb_define_method(cXMLWriter, "start_element_ns", rxml_writer_start_element_ns, -1);
1049
+ rb_define_method(cXMLWriter, "end_element", rxml_writer_end_element, 0);
1050
+ rb_define_method(cXMLWriter, "full_end_element", rxml_writer_full_end_element, 0);
1051
+ rb_define_method(cXMLWriter, "start_document", rxml_writer_start_document, -1);
1052
+ rb_define_method(cXMLWriter, "end_document", rxml_writer_end_document, 0);
1053
+ # if LIBXML_VERSION >= 20607
1054
+ rb_define_method(cXMLWriter, "start_comment", rxml_writer_start_comment, 0);
1055
+ rb_define_method(cXMLWriter, "end_comment", rxml_writer_end_comment, 0);
1056
+ # endif /* LIBXML_VERSION >= 20607 */
1057
+ rb_define_method(cXMLWriter, "start_pi", rxml_writer_start_pi, 1);
1058
+ rb_define_method(cXMLWriter, "end_pi", rxml_writer_end_pi, 0);
1059
+
1060
+ /* full tag at once */
1061
+ rb_define_method(cXMLWriter, "write_attribute", rxml_writer_write_attribute, 2);
1062
+ rb_define_method(cXMLWriter, "write_attribute_ns", rxml_writer_write_attribute_ns, -1);
1063
+ rb_define_method(cXMLWriter, "write_comment", rxml_writer_write_comment, 1);
1064
+ rb_define_method(cXMLWriter, "write_cdata", rxml_writer_write_cdata, 1);
1065
+ rb_define_method(cXMLWriter, "write_element", rxml_writer_write_element, -1);
1066
+ rb_define_method(cXMLWriter, "write_element_ns", rxml_writer_write_element_ns, -1);
1067
+ rb_define_method(cXMLWriter, "write_pi", rxml_writer_write_pi, 2);
1068
+
1069
+ rb_define_method(cXMLWriter, "result", rxml_writer_result, 0);
1070
+ }
1071
+
1072
+ #endif /* LIBXML_WRITER_ENABLED */