libxml-ruby 0.9.7-x86-mswin32-60 → 0.9.8-x86-mswin32-60

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. data/CHANGES +53 -0
  2. data/Rakefile +1 -0
  3. data/ext/libxml/build.log +4 -0
  4. data/ext/libxml/cbg.c +86 -86
  5. data/ext/libxml/libxml.c +878 -876
  6. data/ext/libxml/ruby_libxml.h +8 -4
  7. data/ext/libxml/ruby_xml_attr.c +36 -168
  8. data/ext/libxml/ruby_xml_attr.h +2 -4
  9. data/ext/libxml/ruby_xml_attr_decl.c +177 -0
  10. data/ext/libxml/ruby_xml_attr_decl.h +13 -0
  11. data/ext/libxml/ruby_xml_attributes.c +29 -20
  12. data/ext/libxml/ruby_xml_document.c +895 -898
  13. data/ext/libxml/ruby_xml_dtd.c +18 -1
  14. data/ext/libxml/ruby_xml_dtd.h +1 -0
  15. data/ext/libxml/ruby_xml_encoding.c +116 -0
  16. data/ext/libxml/ruby_xml_encoding.h +12 -0
  17. data/ext/libxml/ruby_xml_error.c +8 -2
  18. data/ext/libxml/ruby_xml_html_parser.c +53 -74
  19. data/ext/libxml/ruby_xml_html_parser.h +2 -3
  20. data/ext/libxml/ruby_xml_html_parser_context.c +145 -0
  21. data/ext/libxml/ruby_xml_html_parser_context.h +12 -0
  22. data/ext/libxml/ruby_xml_html_parser_options.c +48 -0
  23. data/ext/libxml/ruby_xml_html_parser_options.h +12 -0
  24. data/ext/libxml/ruby_xml_input_cbg.c +1 -1
  25. data/ext/libxml/ruby_xml_io.c +30 -0
  26. data/ext/libxml/ruby_xml_io.h +9 -0
  27. data/ext/libxml/ruby_xml_namespace.c +34 -16
  28. data/ext/libxml/ruby_xml_namespace.h +2 -2
  29. data/ext/libxml/ruby_xml_namespaces.c +6 -6
  30. data/ext/libxml/ruby_xml_node.c +1367 -1324
  31. data/ext/libxml/ruby_xml_node.h +2 -2
  32. data/ext/libxml/ruby_xml_parser.c +26 -78
  33. data/ext/libxml/ruby_xml_parser.h +1 -1
  34. data/ext/libxml/ruby_xml_parser_context.c +284 -13
  35. data/ext/libxml/ruby_xml_parser_context.h +1 -2
  36. data/ext/libxml/ruby_xml_parser_options.c +75 -0
  37. data/ext/libxml/ruby_xml_parser_options.h +14 -0
  38. data/ext/libxml/ruby_xml_reader.c +277 -183
  39. data/ext/libxml/ruby_xml_sax_parser.c +60 -57
  40. data/ext/libxml/ruby_xml_xpath_context.c +43 -8
  41. data/ext/libxml/ruby_xml_xpath_expression.c +6 -0
  42. data/ext/libxml/ruby_xml_xpath_object.c +107 -95
  43. data/ext/libxml/ruby_xml_xpath_object.h +9 -1
  44. data/ext/libxml/ruby_xml_xpointer.c +107 -107
  45. data/ext/libxml/version.h +2 -2
  46. data/ext/mingw/libxml_ruby.dll.a +0 -0
  47. data/ext/mingw/libxml_ruby.so +0 -0
  48. data/ext/vc/libxml_ruby.vcproj +43 -3
  49. data/lib/libxml.rb +2 -3
  50. data/lib/libxml/attr.rb +71 -2
  51. data/lib/libxml/attr_decl.rb +81 -0
  52. data/lib/libxml/document.rb +78 -14
  53. data/lib/libxml/html_parser.rb +75 -42
  54. data/lib/libxml/node.rb +11 -0
  55. data/lib/libxml/parser.rb +106 -62
  56. data/lib/libxml/reader.rb +12 -0
  57. data/lib/libxml/sax_parser.rb +42 -52
  58. data/lib/libxml/xpath_object.rb +15 -0
  59. data/test/model/atom.xml +12 -12
  60. data/test/model/bands.xml +4 -4
  61. data/test/model/books.xml +146 -147
  62. data/test/model/merge_bug_data.xml +1 -1
  63. data/test/model/rubynet.xml +1 -0
  64. data/test/model/shiporder.rng +1 -1
  65. data/test/model/shiporder.xml +22 -22
  66. data/test/model/shiporder.xsd +30 -30
  67. data/test/model/xinclude.xml +1 -1
  68. data/test/{tc_node_attr.rb → tc_attr.rb} +1 -1
  69. data/test/tc_attr_decl.rb +131 -0
  70. data/test/tc_deprecated_require.rb +1 -3
  71. data/test/tc_document.rb +13 -3
  72. data/test/tc_document_write.rb +5 -5
  73. data/test/tc_dtd.rb +13 -5
  74. data/test/tc_html_parser.rb +14 -26
  75. data/test/tc_node_cdata.rb +1 -3
  76. data/test/tc_node_comment.rb +2 -4
  77. data/test/tc_node_edit.rb +2 -3
  78. data/test/tc_node_text.rb +35 -1
  79. data/test/tc_node_write.rb +3 -3
  80. data/test/tc_node_xlink.rb +2 -4
  81. data/test/tc_parser.rb +163 -70
  82. data/test/tc_parser_context.rb +103 -42
  83. data/test/tc_reader.rb +173 -45
  84. data/test/tc_relaxng.rb +2 -2
  85. data/test/tc_sax_parser.rb +48 -52
  86. data/test/tc_schema.rb +2 -2
  87. data/test/tc_xpath.rb +37 -6
  88. data/test/tc_xpath_context.rb +7 -1
  89. data/test/tc_xpath_expression.rb +1 -3
  90. data/test/tc_xpointer.rb +1 -3
  91. data/test/test_suite.rb +2 -3
  92. metadata +20 -13
  93. data/ext/libxml/ruby_xml_input.c +0 -329
  94. data/ext/libxml/ruby_xml_input.h +0 -20
  95. data/lib/libxml/parser_context.rb +0 -17
  96. data/lib/libxml/parser_options.rb +0 -25
  97. data/test/model/simple.xml +0 -7
  98. data/test/tc_input.rb +0 -13
  99. data/test/tc_well_formed.rb +0 -11
@@ -5,7 +5,15 @@
5
5
 
6
6
  extern VALUE cXMLXPathObject;
7
7
 
8
+ typedef struct rxml_xpath_object
9
+ {
10
+ xmlDocPtr xdoc;
11
+ xmlXPathObjectPtr xpop;
12
+ VALUE nsnodes;
13
+ } rxml_xpath_object;
14
+
15
+
8
16
  void ruby_init_xml_xpath_object(void);
9
- VALUE rxml_xpath_object_wrap(xmlXPathObjectPtr xpop);
17
+ VALUE rxml_xpath_object_wrap(xmlDocPtr xdoc, xmlXPathObjectPtr xpop);
10
18
 
11
19
  #endif
@@ -1,107 +1,107 @@
1
- /* $Id: ruby_xml_xpointer.c 650 2008-11-30 03:40:22Z cfis $ */
2
-
3
- /* Please see the LICENSE file for copyright and distribution information */
4
-
5
- #include "ruby_libxml.h"
6
- #include "ruby_xml_xpointer.h"
7
-
8
- VALUE cXMLXPointer;
9
-
10
- /*
11
- * Document-class: LibXML::XML::XPointer
12
- *
13
- * The XML::Pointer class provides a standards based API for searching an xml document.
14
- * XPointer is based on the XML Path Language (XML::XPath) and is documented
15
- * at http://www.w3.org/TR/WD-xptr.
16
- */
17
-
18
- static VALUE rxml_xpointer_point(VALUE class, VALUE rnode, VALUE xptr_str)
19
- {
20
- #ifdef LIBXML_XPTR_ENABLED
21
- xmlNodePtr xnode;
22
- xmlXPathContextPtr xctxt;
23
- xmlXPathObjectPtr xpop;
24
-
25
- VALUE context;
26
- VALUE result;
27
- VALUE argv[1];
28
-
29
- Check_Type(xptr_str, T_STRING);
30
- if (rb_obj_is_kind_of(rnode, cXMLNode) == Qfalse)
31
- rb_raise(rb_eTypeError, "require an XML::Node object");
32
-
33
- Data_Get_Struct(rnode, xmlNode, xnode);
34
-
35
- argv[0] = rb_funcall(rnode, rb_intern("doc"), 0);
36
- context = rb_class_new_instance(1, argv, cXMLXPathContext);
37
- Data_Get_Struct(context, xmlXPathContext, xctxt);
38
-
39
- xpop = xmlXPtrEval((xmlChar*)StringValuePtr(xptr_str), xctxt);
40
- if (!xpop)
41
- rxml_raise(&xmlLastError);
42
-
43
- result = rxml_xpath_object_wrap(xpop);
44
- rb_iv_set(result, "@context", context);
45
-
46
- return(result);
47
- #else
48
- rb_warn("libxml was compiled without XPointer support");
49
- return (Qfalse);
50
- #endif
51
- }
52
-
53
- VALUE rxml_xpointer_point2(VALUE node, VALUE xptr_str)
54
- {
55
- return (rxml_xpointer_point(cXMLXPointer, node, xptr_str));
56
- }
57
-
58
- /*
59
- * call-seq:
60
- * XML::XPointer.range(start_node, end_node) -> xpath
61
- *
62
- * Create an xpath representing the range between the supplied
63
- * start and end node.
64
- */
65
- static VALUE rxml_xpointer_range(VALUE class, VALUE rstart, VALUE rend)
66
- {
67
- #ifdef LIBXML_XPTR_ENABLED
68
- xmlNodePtr start, end;
69
- VALUE rxxp;
70
- xmlXPathObjectPtr xpath;
71
-
72
- if (rb_obj_is_kind_of(rstart, cXMLNode) == Qfalse)
73
- rb_raise(rb_eTypeError, "require an XML::Node object as a starting point");
74
- if (rb_obj_is_kind_of(rend, cXMLNode) == Qfalse)
75
- rb_raise(rb_eTypeError, "require an XML::Node object as an ending point");
76
-
77
- Data_Get_Struct(rstart, xmlNode, start);
78
- if (start == NULL)
79
- return(Qnil);
80
-
81
- Data_Get_Struct(rend, xmlNode, end);
82
- if (end == NULL)
83
- return(Qnil);
84
-
85
- xpath = xmlXPtrNewRangeNodes(start, end);
86
- if (xpath == NULL)
87
- rb_fatal("You shouldn't be able to have this happen");
88
-
89
- rxxp = rxml_xpath_object_wrap(xpath);
90
- return(rxxp);
91
- #else
92
- rb_warn("libxml was compiled without XPointer support");
93
- return (Qfalse);
94
- #endif
95
- }
96
-
97
- // Rdoc needs to know
98
- #ifdef RDOC_NEVER_DEFINED
99
- mLibXML = rb_define_module("LibXML");
100
- mXML = rb_define_module_under(mLibXML, "XML");
101
- #endif
102
-
103
- void ruby_init_xml_xpointer(void)
104
- {
105
- cXMLXPointer = rb_define_class_under(mXML, "XPointer", rb_cObject);
106
- rb_define_singleton_method(cXMLXPointer, "range", rxml_xpointer_range, 2);
107
- }
1
+ /* $Id: ruby_xml_xpointer.c 739 2009-01-23 03:42:09Z cfis $ */
2
+
3
+ /* Please see the LICENSE file for copyright and distribution information */
4
+
5
+ #include "ruby_libxml.h"
6
+ #include "ruby_xml_xpointer.h"
7
+
8
+ VALUE cXMLXPointer;
9
+
10
+ /*
11
+ * Document-class: LibXML::XML::XPointer
12
+ *
13
+ * The XML::Pointer class provides a standards based API for searching an xml document.
14
+ * XPointer is based on the XML Path Language (XML::XPath) and is documented
15
+ * at http://www.w3.org/TR/WD-xptr.
16
+ */
17
+
18
+ static VALUE rxml_xpointer_point(VALUE class, VALUE rnode, VALUE xptr_str)
19
+ {
20
+ #ifdef LIBXML_XPTR_ENABLED
21
+ xmlNodePtr xnode;
22
+ xmlXPathContextPtr xctxt;
23
+ xmlXPathObjectPtr xpop;
24
+
25
+ VALUE context;
26
+ VALUE result;
27
+ VALUE argv[1];
28
+
29
+ Check_Type(xptr_str, T_STRING);
30
+ if (rb_obj_is_kind_of(rnode, cXMLNode) == Qfalse)
31
+ rb_raise(rb_eTypeError, "require an XML::Node object");
32
+
33
+ Data_Get_Struct(rnode, xmlNode, xnode);
34
+
35
+ argv[0] = rb_funcall(rnode, rb_intern("doc"), 0);
36
+ context = rb_class_new_instance(1, argv, cXMLXPathContext);
37
+ Data_Get_Struct(context, xmlXPathContext, xctxt);
38
+
39
+ xpop = xmlXPtrEval((xmlChar*)StringValuePtr(xptr_str), xctxt);
40
+ if (!xpop)
41
+ rxml_raise(&xmlLastError);
42
+
43
+ result = rxml_xpath_object_wrap(xnode->doc, xpop);
44
+ rb_iv_set(result, "@context", context);
45
+
46
+ return(result);
47
+ #else
48
+ rb_warn("libxml was compiled without XPointer support");
49
+ return (Qfalse);
50
+ #endif
51
+ }
52
+
53
+ VALUE rxml_xpointer_point2(VALUE node, VALUE xptr_str)
54
+ {
55
+ return (rxml_xpointer_point(cXMLXPointer, node, xptr_str));
56
+ }
57
+
58
+ /*
59
+ * call-seq:
60
+ * XML::XPointer.range(start_node, end_node) -> xpath
61
+ *
62
+ * Create an xpath representing the range between the supplied
63
+ * start and end node.
64
+ */
65
+ static VALUE rxml_xpointer_range(VALUE class, VALUE rstart, VALUE rend)
66
+ {
67
+ #ifdef LIBXML_XPTR_ENABLED
68
+ xmlNodePtr start, end;
69
+ VALUE rxxp;
70
+ xmlXPathObjectPtr xpath;
71
+
72
+ if (rb_obj_is_kind_of(rstart, cXMLNode) == Qfalse)
73
+ rb_raise(rb_eTypeError, "require an XML::Node object as a starting point");
74
+ if (rb_obj_is_kind_of(rend, cXMLNode) == Qfalse)
75
+ rb_raise(rb_eTypeError, "require an XML::Node object as an ending point");
76
+
77
+ Data_Get_Struct(rstart, xmlNode, start);
78
+ if (start == NULL)
79
+ return(Qnil);
80
+
81
+ Data_Get_Struct(rend, xmlNode, end);
82
+ if (end == NULL)
83
+ return(Qnil);
84
+
85
+ xpath = xmlXPtrNewRangeNodes(start, end);
86
+ if (xpath == NULL)
87
+ rb_fatal("You shouldn't be able to have this happen");
88
+
89
+ rxxp = rxml_xpath_object_wrap(start->doc, xpath);
90
+ return(rxxp);
91
+ #else
92
+ rb_warn("libxml was compiled without XPointer support");
93
+ return (Qfalse);
94
+ #endif
95
+ }
96
+
97
+ // Rdoc needs to know
98
+ #ifdef RDOC_NEVER_DEFINED
99
+ mLibXML = rb_define_module("LibXML");
100
+ mXML = rb_define_module_under(mLibXML, "XML");
101
+ #endif
102
+
103
+ void ruby_init_xml_xpointer(void)
104
+ {
105
+ cXMLXPointer = rb_define_class_under(mXML, "XPointer", rb_cObject);
106
+ rb_define_singleton_method(cXMLXPointer, "range", rxml_xpointer_range, 2);
107
+ }
@@ -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 "0.9.7"
4
+ #define RUBY_LIBXML_VERSION "0.9.8"
5
5
  #define RUBY_LIBXML_VERNUM 0
6
6
  #define RUBY_LIBXML_VER_MAJ 0
7
7
  #define RUBY_LIBXML_VER_MIN 9
8
- #define RUBY_LIBXML_VER_MIC 7
8
+ #define RUBY_LIBXML_VER_MIC 8
9
9
  #define RUBY_LIBXML_VER_PATCH 0
Binary file
Binary file
@@ -63,7 +63,7 @@
63
63
  <Tool
64
64
  Name="VCLinkerTool"
65
65
  AdditionalDependencies="msvcrt-ruby18.lib libxml2.lib"
66
- OutputFile="C:\Development\ruby\lib\ruby\gems\1.8\gems\libxml-ruby-0.9.6-x86-mswin32-60\lib\$(ProjectName).so"
66
+ OutputFile="C:\Development\ruby\lib\ruby\gems\1.8\gems\libxml-ruby-0.9.8-x86-mswin32-60\lib\$(ProjectName).so"
67
67
  LinkIncremental="2"
68
68
  AdditionalLibraryDirectories="C:\Development\ruby\lib;C:\Development\msys\local\lib"
69
69
  GenerateDebugInformation="true"
@@ -193,6 +193,10 @@
193
193
  RelativePath="..\libxml\ruby_xml_attr.c"
194
194
  >
195
195
  </File>
196
+ <File
197
+ RelativePath="..\libxml\ruby_xml_attr_decl.c"
198
+ >
199
+ </File>
196
200
  <File
197
201
  RelativePath="..\libxml\ruby_xml_attributes.c"
198
202
  >
@@ -205,6 +209,10 @@
205
209
  RelativePath="..\libxml\ruby_xml_dtd.c"
206
210
  >
207
211
  </File>
212
+ <File
213
+ RelativePath="..\libxml\ruby_xml_encoding.c"
214
+ >
215
+ </File>
208
216
  <File
209
217
  RelativePath="..\libxml\ruby_xml_error.c"
210
218
  >
@@ -214,13 +222,21 @@
214
222
  >
215
223
  </File>
216
224
  <File
217
- RelativePath="..\libxml\ruby_xml_input.c"
225
+ RelativePath="..\libxml\ruby_xml_html_parser_context.c"
226
+ >
227
+ </File>
228
+ <File
229
+ RelativePath="..\libxml\ruby_xml_html_parser_options.c"
218
230
  >
219
231
  </File>
220
232
  <File
221
233
  RelativePath="..\libxml\ruby_xml_input_cbg.c"
222
234
  >
223
235
  </File>
236
+ <File
237
+ RelativePath="..\libxml\ruby_xml_io.c"
238
+ >
239
+ </File>
224
240
  <File
225
241
  RelativePath="..\libxml\ruby_xml_namespace.c"
226
242
  >
@@ -241,6 +257,10 @@
241
257
  RelativePath="..\libxml\ruby_xml_parser_context.c"
242
258
  >
243
259
  </File>
260
+ <File
261
+ RelativePath="..\libxml\ruby_xml_parser_options.c"
262
+ >
263
+ </File>
244
264
  <File
245
265
  RelativePath="..\libxml\ruby_xml_reader.c"
246
266
  >
@@ -303,6 +323,10 @@
303
323
  RelativePath="..\libxml\ruby_xml_attr.h"
304
324
  >
305
325
  </File>
326
+ <File
327
+ RelativePath="..\libxml\ruby_xml_attr_decl.h"
328
+ >
329
+ </File>
306
330
  <File
307
331
  RelativePath="..\libxml\ruby_xml_attributes.h"
308
332
  >
@@ -315,6 +339,10 @@
315
339
  RelativePath="..\libxml\ruby_xml_dtd.h"
316
340
  >
317
341
  </File>
342
+ <File
343
+ RelativePath="..\libxml\ruby_xml_encoding.h"
344
+ >
345
+ </File>
318
346
  <File
319
347
  RelativePath="..\libxml\ruby_xml_error.h"
320
348
  >
@@ -324,13 +352,21 @@
324
352
  >
325
353
  </File>
326
354
  <File
327
- RelativePath="..\libxml\ruby_xml_input.h"
355
+ RelativePath="..\libxml\ruby_xml_html_parser_context.h"
356
+ >
357
+ </File>
358
+ <File
359
+ RelativePath="..\libxml\ruby_xml_html_parser_options.h"
328
360
  >
329
361
  </File>
330
362
  <File
331
363
  RelativePath="..\libxml\ruby_xml_input_cbg.h"
332
364
  >
333
365
  </File>
366
+ <File
367
+ RelativePath="..\libxml\ruby_xml_io.h"
368
+ >
369
+ </File>
334
370
  <File
335
371
  RelativePath="..\libxml\ruby_xml_namespace.h"
336
372
  >
@@ -351,6 +387,10 @@
351
387
  RelativePath="..\libxml\ruby_xml_parser_context.h"
352
388
  >
353
389
  </File>
390
+ <File
391
+ RelativePath="..\libxml\ruby_xml_parser_options.h"
392
+ >
393
+ </File>
354
394
  <File
355
395
  RelativePath="..\libxml\ruby_xml_reader.h"
356
396
  >
@@ -1,4 +1,4 @@
1
- # $Id: libxml.rb 666 2008-12-07 00:16:50Z cfis $
1
+ # $Id: libxml.rb 710 2009-01-20 05:30:51Z cfis $
2
2
  # Please see the LICENSE file for copyright and distribution information
3
3
 
4
4
  # If running on Windows, then add the current directory to the PATH
@@ -14,8 +14,6 @@ require 'libxml_ruby'
14
14
  # Load Ruby supporting code.
15
15
  require 'libxml/error'
16
16
  require 'libxml/parser'
17
- require 'libxml/parser_options'
18
- require 'libxml/parser_context'
19
17
  require 'libxml/document'
20
18
  require 'libxml/namespaces'
21
19
  require 'libxml/namespace'
@@ -23,6 +21,7 @@ require 'libxml/node'
23
21
  require 'libxml/ns'
24
22
  require 'libxml/attributes'
25
23
  require 'libxml/attr'
24
+ require 'libxml/attr_decl'
26
25
  require 'libxml/tree'
27
26
  require 'libxml/reader'
28
27
  require 'libxml/html_parser'
@@ -6,6 +6,75 @@ module LibXML
6
6
  class Attr
7
7
  include Enumerable
8
8
 
9
+ # call-seq:
10
+ # attr.child? -> (true|false)
11
+ #
12
+ # Returns whether this attribute has child attributes.
13
+ #
14
+ def child?
15
+ not self.children.nil?
16
+ end
17
+
18
+ # call-seq:
19
+ # attr.doc? -> (true|false)
20
+ #
21
+ # Determine whether this attribute is associated with an
22
+ # XML::Document.
23
+ def doc?
24
+ not self.doc.nil?
25
+ end
26
+
27
+ # call-seq:
28
+ # attr.last? -> (true|false)
29
+ #
30
+ # Determine whether this is the last attribute.
31
+ def last?
32
+ self.last.nil?
33
+ end
34
+
35
+ # call-seq:
36
+ # attr.next? -> (true|false)
37
+ #
38
+ # Determine whether there is a next attribute.
39
+ def next?
40
+ not self.next.nil?
41
+ end
42
+
43
+ # call-seq:
44
+ # attr.ns? -> (true|false)
45
+ #
46
+ # Determine whether this attribute has an associated
47
+ # namespace.
48
+ def ns?
49
+ not self.ns.nil?
50
+ end
51
+
52
+ #
53
+ # call-seq:
54
+ # attr.parent? -> (true|false)
55
+ #
56
+ # Determine whether this attribute has a parent.
57
+ def parent?
58
+ not self.parent.nil?
59
+ end
60
+
61
+ # call-seq:
62
+ # attr.prev? -> (true|false)
63
+ #
64
+ # Determine whether there is a previous attribute.
65
+ def prev?
66
+ not self.prev.nil?
67
+ end
68
+
69
+ # Returns this node's type name
70
+ def node_type_name
71
+ if node_type == Node::ATTRIBUTE_NODE
72
+ 'attribute'
73
+ else
74
+ raise(UnknownType, "Unknown node type: %n", node.node_type);
75
+ end
76
+ end
77
+
9
78
  # Iterates nodes and attributes
10
79
  def siblings(node, &blk)
11
80
  if n = node
@@ -13,9 +82,9 @@ module LibXML
13
82
  blk.call(n)
14
83
  break unless n = n.next
15
84
  end
16
- end
85
+ end
17
86
  end
18
-
87
+
19
88
  def each_sibling(&blk)
20
89
  siblings(self,&blk)
21
90
  end