rexml 3.2.0 → 3.2.5

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rexml might be problematic. Click here for more details.

data/lib/rexml/rexml.rb CHANGED
@@ -1,30 +1,35 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  # frozen_string_literal: false
3
- # REXML is an XML toolkit for Ruby[http://www.ruby-lang.org], in Ruby.
4
- #
5
- # REXML is a _pure_ Ruby, XML 1.0 conforming,
6
- # non-validating[http://www.w3.org/TR/2004/REC-xml-20040204/#sec-conformance]
7
- # toolkit with an intuitive API. REXML passes 100% of the non-validating Oasis
8
- # tests[http://www.oasis-open.org/committees/xml-conformance/xml-test-suite.shtml],
9
- # and provides tree, stream, SAX2, pull, and lightweight APIs. REXML also
10
- # includes a full XPath[http://www.w3c.org/tr/xpath] 1.0 implementation. Since
11
- # Ruby 1.8, REXML is included in the standard Ruby distribution.
12
- #
13
- # Main page:: http://www.germane-software.com/software/rexml
14
- # Author:: Sean Russell <serATgermaneHYPHENsoftwareDOTcom>
15
- # Date:: 2008/019
16
- # Version:: 3.1.7.3
17
- #
18
- # This API documentation can be downloaded from the REXML home page, or can
19
- # be accessed online[http://www.germane-software.com/software/rexml_doc]
20
- #
21
- # A tutorial is available in the REXML distribution in docs/tutorial.html,
22
- # or can be accessed
23
- # online[http://www.germane-software.com/software/rexml/docs/tutorial.html]
3
+ #
4
+ # \Module \REXML provides classes and methods for parsing,
5
+ # editing, and generating XML.
6
+ #
7
+ # == Implementation
8
+ #
9
+ # \REXML:
10
+ # - Is pure Ruby.
11
+ # - Provides tree, stream, SAX2, pull, and lightweight APIs.
12
+ # - Conforms to {XML version 1.0}[https://www.w3.org/TR/REC-xml/].
13
+ # - Fully implements {XPath version 1.0}[http://www.w3c.org/tr/xpath].
14
+ # - Is {non-validating}[https://www.w3.org/TR/xml/].
15
+ # - Passes 100% of the non-validating {Oasis tests}[http://www.oasis-open.org/committees/xml-conformance/xml-test-suite.shtml].
16
+ #
17
+ # == In a Hurry?
18
+ #
19
+ # If you're somewhat familiar with XML
20
+ # and have a particular task in mind,
21
+ # you may want to see {the tasks pages}[doc/rexml/tasks/tocs/master_toc_rdoc.html].
22
+ #
23
+ # == API
24
+ #
25
+ # Among the most important classes for using \REXML are:
26
+ # - REXML::Document.
27
+ # - REXML::Element.
28
+ #
24
29
  module REXML
25
30
  COPYRIGHT = "Copyright © 2001-2008 Sean Russell <ser@germane-software.com>"
26
31
  DATE = "2008/019"
27
- VERSION = "3.2.0"
32
+ VERSION = "3.2.5"
28
33
  REVISION = ""
29
34
 
30
35
  Copyright = COPYRIGHT
data/lib/rexml/source.rb CHANGED
@@ -200,7 +200,7 @@ module REXML
200
200
  end
201
201
  rv = super
202
202
  end
203
- rv.taint
203
+ rv.taint if RUBY_VERSION < '2.7'
204
204
  rv
205
205
  end
206
206
 
@@ -228,7 +228,7 @@ module REXML
228
228
  @source = nil
229
229
  end
230
230
  end
231
- rv.taint
231
+ rv.taint if RUBY_VERSION < '2.7'
232
232
  rv
233
233
  end
234
234
 
data/lib/rexml/text.rb CHANGED
@@ -109,7 +109,7 @@ module REXML
109
109
  @string = arg.instance_variable_get(:@string).dup
110
110
  @raw = arg.raw
111
111
  @entity_filter = arg.instance_variable_get(:@entity_filter)
112
- elsif
112
+ else
113
113
  raise "Illegal argument of type #{arg.type} for Text constructor (#{arg})"
114
114
  end
115
115
 
@@ -137,7 +137,7 @@ module REXML
137
137
  case c.ord
138
138
  when *VALID_CHAR
139
139
  else
140
- raise "Illegal character #{c.inspect} in raw string \"#{string}\""
140
+ raise "Illegal character #{c.inspect} in raw string #{string.inspect}"
141
141
  end
142
142
  end
143
143
  else
@@ -145,7 +145,7 @@ module REXML
145
145
  case c.unpack('U')
146
146
  when *VALID_CHAR
147
147
  else
148
- raise "Illegal character #{c.inspect} in raw string \"#{string}\""
148
+ raise "Illegal character #{c.inspect} in raw string #{string.inspect}"
149
149
  end
150
150
  end
151
151
  end
@@ -154,13 +154,13 @@ module REXML
154
154
  # context sensitive
155
155
  string.scan(pattern) do
156
156
  if $1[-1] != ?;
157
- raise "Illegal character '#{$1}' in raw string \"#{string}\""
157
+ raise "Illegal character #{$1.inspect} in raw string #{string.inspect}"
158
158
  elsif $1[0] == ?&
159
159
  if $5 and $5[0] == ?#
160
160
  case ($5[1] == ?x ? $5[2..-1].to_i(16) : $5[1..-1].to_i)
161
161
  when *VALID_CHAR
162
162
  else
163
- raise "Illegal character '#{$1}' in raw string \"#{string}\""
163
+ raise "Illegal character #{$1.inspect} in raw string #{string.inspect}"
164
164
  end
165
165
  # FIXME: below can't work but this needs API change.
166
166
  # elsif @parent and $3 and !SUBSTITUTES.include?($1)
data/lib/rexml/xmldecl.rb CHANGED
@@ -26,6 +26,7 @@ module REXML
26
26
  self.encoding = version.encoding
27
27
  @writeencoding = version.writeencoding
28
28
  @standalone = version.standalone
29
+ @writethis = version.writethis
29
30
  else
30
31
  super()
31
32
  @version = version
@@ -1,43 +1,51 @@
1
1
  # frozen_string_literal: false
2
+
3
+ require "pp"
4
+
2
5
  require_relative 'namespace'
3
6
  require_relative 'xmltokens'
4
7
  require_relative 'attribute'
5
- require_relative 'syncenumerator'
6
8
  require_relative 'parsers/xpathparser'
7
9
 
8
- class Object
9
- # provides a unified +clone+ operation, for REXML::XPathParser
10
- # to use across multiple Object types
11
- def dclone
12
- clone
13
- end
14
- end
15
- class Symbol
16
- # provides a unified +clone+ operation, for REXML::XPathParser
17
- # to use across multiple Object types
18
- def dclone ; self ; end
19
- end
20
- class Integer
21
- # provides a unified +clone+ operation, for REXML::XPathParser
22
- # to use across multiple Object types
23
- def dclone ; self ; end
24
- end
25
- class Float
26
- # provides a unified +clone+ operation, for REXML::XPathParser
27
- # to use across multiple Object types
28
- def dclone ; self ; end
29
- end
30
- class Array
31
- # provides a unified +clone+ operation, for REXML::XPathParser
32
- # to use across multiple Object+ types
33
- def dclone
34
- klone = self.clone
35
- klone.clear
36
- self.each{|v| klone << v.dclone}
37
- klone
10
+ module REXML
11
+ module DClonable
12
+ refine Object do
13
+ # provides a unified +clone+ operation, for REXML::XPathParser
14
+ # to use across multiple Object types
15
+ def dclone
16
+ clone
17
+ end
18
+ end
19
+ refine Symbol do
20
+ # provides a unified +clone+ operation, for REXML::XPathParser
21
+ # to use across multiple Object types
22
+ def dclone ; self ; end
23
+ end
24
+ refine Integer do
25
+ # provides a unified +clone+ operation, for REXML::XPathParser
26
+ # to use across multiple Object types
27
+ def dclone ; self ; end
28
+ end
29
+ refine Float do
30
+ # provides a unified +clone+ operation, for REXML::XPathParser
31
+ # to use across multiple Object types
32
+ def dclone ; self ; end
33
+ end
34
+ refine Array do
35
+ # provides a unified +clone+ operation, for REXML::XPathParser
36
+ # to use across multiple Object+ types
37
+ def dclone
38
+ klone = self.clone
39
+ klone.clear
40
+ self.each{|v| klone << v.dclone}
41
+ klone
42
+ end
43
+ end
38
44
  end
39
45
  end
40
46
 
47
+ using REXML::DClonable
48
+
41
49
  module REXML
42
50
  # You don't want to use this class. Really. Use XPath, which is a wrapper
43
51
  # for this class. Believe me. You don't want to poke around in here.
@@ -47,7 +55,10 @@ module REXML
47
55
  include XMLTokens
48
56
  LITERAL = /^'([^']*)'|^"([^"]*)"/u
49
57
 
58
+ DEBUG = (ENV["REXML_XPATH_PARSER_DEBUG"] == "true")
59
+
50
60
  def initialize(strict: false)
61
+ @debug = DEBUG
51
62
  @parser = REXML::Parsers::XPathParser.new
52
63
  @namespaces = nil
53
64
  @variables = {}
@@ -135,7 +146,7 @@ module REXML
135
146
  when Array # nodeset
136
147
  unnode(result)
137
148
  else
138
- result
149
+ [result]
139
150
  end
140
151
  end
141
152
 
@@ -162,10 +173,10 @@ module REXML
162
173
  # Expr takes a stack of path elements and a set of nodes (either a Parent
163
174
  # or an Array and returns an Array of matching nodes
164
175
  def expr( path_stack, nodeset, context=nil )
165
- # enter(:expr, path_stack, nodeset)
176
+ enter(:expr, path_stack, nodeset) if @debug
166
177
  return nodeset if path_stack.length == 0 || nodeset.length == 0
167
178
  while path_stack.length > 0
168
- # trace(:while, path_stack, nodeset)
179
+ trace(:while, path_stack, nodeset) if @debug
169
180
  if nodeset.length == 0
170
181
  path_stack.clear
171
182
  return []
@@ -184,7 +195,7 @@ module REXML
184
195
  child(nodeset)
185
196
  end
186
197
  when :literal
187
- # trace(:literal, path_stack, nodeset)
198
+ trace(:literal, path_stack, nodeset) if @debug
188
199
  return path_stack.shift
189
200
  when :attribute
190
201
  nodeset = step(path_stack, any_type: :attribute) do
@@ -335,26 +346,24 @@ module REXML
335
346
  var_name = path_stack.shift
336
347
  return [@variables[var_name]]
337
348
 
338
- # :and, :or, :eq, :neq, :lt, :lteq, :gt, :gteq
339
- # TODO: Special case for :or and :and -- not evaluate the right
340
- # operand if the left alone determines result (i.e. is true for
341
- # :or and false for :and).
342
- when :eq, :neq, :lt, :lteq, :gt, :gteq, :or
349
+ when :eq, :neq, :lt, :lteq, :gt, :gteq
343
350
  left = expr( path_stack.shift, nodeset.dup, context )
344
351
  right = expr( path_stack.shift, nodeset.dup, context )
345
352
  res = equality_relational_compare( left, op, right )
346
- # trace(op, left, right, res)
353
+ trace(op, left, right, res) if @debug
347
354
  return res
348
355
 
356
+ when :or
357
+ left = expr(path_stack.shift, nodeset.dup, context)
358
+ return true if Functions.boolean(left)
359
+ right = expr(path_stack.shift, nodeset.dup, context)
360
+ return Functions.boolean(right)
361
+
349
362
  when :and
350
- left = expr( path_stack.shift, nodeset.dup, context )
351
- return [] unless left
352
- if left.respond_to?(:inject) and !left.inject(false) {|a,b| a | b}
353
- return []
354
- end
355
- right = expr( path_stack.shift, nodeset.dup, context )
356
- res = equality_relational_compare( left, op, right )
357
- return res
363
+ left = expr(path_stack.shift, nodeset.dup, context)
364
+ return false unless Functions.boolean(left)
365
+ right = expr(path_stack.shift, nodeset.dup, context)
366
+ return Functions.boolean(right)
358
367
 
359
368
  when :div, :mod, :mult, :plus, :minus
360
369
  left = expr(path_stack.shift, nodeset, context)
@@ -391,45 +400,48 @@ module REXML
391
400
  when :function
392
401
  func_name = path_stack.shift.tr('-','_')
393
402
  arguments = path_stack.shift
394
- subcontext = context ? nil : { :size => nodeset.size }
395
-
396
- res = []
397
- cont = context
398
- nodeset.each_with_index do |node, i|
399
- if subcontext
400
- if node.is_a?(XPathNode)
401
- subcontext[:node] = node.raw_node
402
- subcontext[:index] = node.position
403
- else
404
- subcontext[:node] = node
405
- subcontext[:index] = i
406
- end
407
- cont = subcontext
408
- end
409
- arg_clone = arguments.dclone
410
- args = arg_clone.collect do |arg|
411
- result = expr( arg, [node], cont )
412
- result = unnode(result) if result.is_a?(Array)
413
- result
403
+
404
+ if nodeset.size != 1
405
+ message = "[BUG] Node set size must be 1 for function call: "
406
+ message += "<#{func_name}>: <#{nodeset.inspect}>: "
407
+ message += "<#{arguments.inspect}>"
408
+ raise message
409
+ end
410
+
411
+ node = nodeset.first
412
+ if context
413
+ target_context = context
414
+ else
415
+ target_context = {:size => nodeset.size}
416
+ if node.is_a?(XPathNode)
417
+ target_context[:node] = node.raw_node
418
+ target_context[:index] = node.position
419
+ else
420
+ target_context[:node] = node
421
+ target_context[:index] = 1
414
422
  end
415
- Functions.context = cont
416
- res << Functions.send( func_name, *args )
417
423
  end
418
- return res
424
+ args = arguments.dclone.collect do |arg|
425
+ result = expr(arg, nodeset, target_context)
426
+ result = unnode(result) if result.is_a?(Array)
427
+ result
428
+ end
429
+ Functions.context = target_context
430
+ return Functions.send(func_name, *args)
419
431
 
420
432
  else
421
433
  raise "[BUG] Unexpected path: <#{op.inspect}>: <#{path_stack.inspect}>"
422
434
  end
423
435
  end # while
424
436
  return nodeset
425
- # ensure
426
- # leave(:expr, path_stack, nodeset)
437
+ ensure
438
+ leave(:expr, path_stack, nodeset) if @debug
427
439
  end
428
440
 
429
441
  def step(path_stack, any_type: :element, order: :forward)
430
442
  nodesets = yield
431
443
  begin
432
- # enter(:step, path_stack, nodesets)
444
+ enter(:step, path_stack, nodesets) if @debug
433
445
  nodesets = node_test(path_stack, nodesets, any_type: any_type)
434
446
  while path_stack[0] == :predicate
435
447
  path_stack.shift # :predicate
@@ -457,13 +469,13 @@ module REXML
457
469
  new_nodeset << XPathNode.new(node, position: new_nodeset.size + 1)
458
470
  end
459
471
  new_nodeset
460
- # ensure
461
- # leave(:step, path_stack, new_nodeset)
472
+ ensure
473
+ leave(:step, path_stack, new_nodeset) if @debug
462
474
  end
463
475
  end
464
476
 
465
477
  def node_test(path_stack, nodesets, any_type: :element)
466
- # enter(:node_test, path_stack, nodesets)
478
+ enter(:node_test, path_stack, nodesets) if @debug
467
479
  operator = path_stack.shift
468
480
  case operator
469
481
  when :qname
@@ -563,8 +575,8 @@ module REXML
563
575
  raise message
564
576
  end
565
577
  new_nodesets
566
- # ensure
567
- # leave(:node_test, path_stack, new_nodesets)
578
+ ensure
579
+ leave(:node_test, path_stack, new_nodesets) if @debug
568
580
  end
569
581
 
570
582
  def filter_nodeset(nodeset)
@@ -577,7 +589,7 @@ module REXML
577
589
  end
578
590
 
579
591
  def evaluate_predicate(expression, nodesets)
580
- # enter(:predicate, expression, nodesets)
592
+ enter(:predicate, expression, nodesets) if @debug
581
593
  new_nodesets = nodesets.collect do |nodeset|
582
594
  new_nodeset = []
583
595
  subcontext = { :size => nodeset.size }
@@ -590,7 +602,7 @@ module REXML
590
602
  subcontext[:index] = index + 1
591
603
  end
592
604
  result = expr(expression.dclone, [node], subcontext)
593
- # trace(:predicate_evaluate, expression, node, subcontext, result)
605
+ trace(:predicate_evaluate, expression, node, subcontext, result) if @debug
594
606
  result = result[0] if result.kind_of? Array and result.length == 1
595
607
  if result.kind_of? Numeric
596
608
  if result == node.position
@@ -611,13 +623,15 @@ module REXML
611
623
  new_nodeset
612
624
  end
613
625
  new_nodesets
614
- # ensure
615
- # leave(:predicate, new_nodesets)
626
+ ensure
627
+ leave(:predicate, new_nodesets) if @debug
616
628
  end
617
629
 
618
630
  def trace(*args)
619
631
  indent = " " * @nest
620
- puts("#{indent}#{args.inspect}")
632
+ PP.pp(args, "").each_line do |line|
633
+ puts("#{indent}#{line}")
634
+ end
621
635
  end
622
636
 
623
637
  def enter(tag, *args)
@@ -798,31 +812,28 @@ module REXML
798
812
  end
799
813
  end
800
814
 
801
- def equality_relational_compare( set1, op, set2 )
815
+ def equality_relational_compare(set1, op, set2)
802
816
  set1 = unnode(set1) if set1.is_a?(Array)
803
817
  set2 = unnode(set2) if set2.is_a?(Array)
818
+
804
819
  if set1.kind_of? Array and set2.kind_of? Array
805
- if set1.size == 0 or set2.size == 0
806
- nd = set1.size==0 ? set2 : set1
807
- rv = nd.collect { |il| compare( il, op, nil ) }
808
- return rv
809
- else
810
- res = []
811
- SyncEnumerator.new( set1, set2 ).each { |i1, i2|
812
- i1 = norm( i1 )
813
- i2 = norm( i2 )
814
- res << compare( i1, op, i2 )
815
- }
816
- return res
820
+ # If both objects to be compared are node-sets, then the
821
+ # comparison will be true if and only if there is a node in the
822
+ # first node-set and a node in the second node-set such that the
823
+ # result of performing the comparison on the string-values of
824
+ # the two nodes is true.
825
+ set1.product(set2).any? do |node1, node2|
826
+ node_string1 = Functions.string(node1)
827
+ node_string2 = Functions.string(node2)
828
+ compare(node_string1, op, node_string2)
817
829
  end
818
- end
819
- # If one is nodeset and other is number, compare number to each item
820
- # in nodeset s.t. number op number(string(item))
821
- # If one is nodeset and other is string, compare string to each item
822
- # in nodeset s.t. string op string(item)
823
- # If one is nodeset and other is boolean, compare boolean to each item
824
- # in nodeset s.t. boolean op boolean(item)
825
- if set1.kind_of? Array or set2.kind_of? Array
830
+ elsif set1.kind_of? Array or set2.kind_of? Array
831
+ # If one is nodeset and other is number, compare number to each item
832
+ # in nodeset s.t. number op number(string(item))
833
+ # If one is nodeset and other is string, compare string to each item
834
+ # in nodeset s.t. string op string(item)
835
+ # If one is nodeset and other is boolean, compare boolean to each item
836
+ # in nodeset s.t. boolean op boolean(item)
826
837
  if set1.kind_of? Array
827
838
  a = set1
828
839
  b = set2
@@ -833,15 +844,23 @@ module REXML
833
844
 
834
845
  case b
835
846
  when true, false
836
- return unnode(a) {|v| compare( Functions::boolean(v), op, b ) }
847
+ each_unnode(a).any? do |unnoded|
848
+ compare(Functions.boolean(unnoded), op, b)
849
+ end
837
850
  when Numeric
838
- return unnode(a) {|v| compare( Functions::number(v), op, b )}
839
- when /^\d+(\.\d+)?$/
840
- b = Functions::number( b )
841
- return unnode(a) {|v| compare( Functions::number(v), op, b )}
851
+ each_unnode(a).any? do |unnoded|
852
+ compare(Functions.number(unnoded), op, b)
853
+ end
854
+ when /\A\d+(\.\d+)?\z/
855
+ b = Functions.number(b)
856
+ each_unnode(a).any? do |unnoded|
857
+ compare(Functions.number(unnoded), op, b)
858
+ end
842
859
  else
843
- b = Functions::string( b )
844
- return unnode(a) { |v| compare( Functions::string(v), op, b ) }
860
+ b = Functions::string(b)
861
+ each_unnode(a).any? do |unnoded|
862
+ compare(Functions::string(unnoded), op, b)
863
+ end
845
864
  end
846
865
  else
847
866
  # If neither is nodeset,
@@ -851,34 +870,52 @@ module REXML
851
870
  # Else, convert to string
852
871
  # Else
853
872
  # Convert both to numbers and compare
854
- set1 = unnode(set1) if set1.is_a?(Array)
855
- set2 = unnode(set2) if set2.is_a?(Array)
856
- s1 = Functions.string(set1)
857
- s2 = Functions.string(set2)
858
- if s1 == 'true' or s1 == 'false' or s2 == 'true' or s2 == 'false'
859
- set1 = Functions::boolean( set1 )
860
- set2 = Functions::boolean( set2 )
873
+ compare(set1, op, set2)
874
+ end
875
+ end
876
+
877
+ def value_type(value)
878
+ case value
879
+ when true, false
880
+ :boolean
881
+ when Numeric
882
+ :number
883
+ when String
884
+ :string
885
+ else
886
+ raise "[BUG] Unexpected value type: <#{value.inspect}>"
887
+ end
888
+ end
889
+
890
+ def normalize_compare_values(a, operator, b)
891
+ a_type = value_type(a)
892
+ b_type = value_type(b)
893
+ case operator
894
+ when :eq, :neq
895
+ if a_type == :boolean or b_type == :boolean
896
+ a = Functions.boolean(a) unless a_type == :boolean
897
+ b = Functions.boolean(b) unless b_type == :boolean
898
+ elsif a_type == :number or b_type == :number
899
+ a = Functions.number(a) unless a_type == :number
900
+ b = Functions.number(b) unless b_type == :number
861
901
  else
862
- if op == :eq or op == :neq
863
- if s1 =~ /^\d+(\.\d+)?$/ or s2 =~ /^\d+(\.\d+)?$/
864
- set1 = Functions::number( s1 )
865
- set2 = Functions::number( s2 )
866
- else
867
- set1 = Functions::string( set1 )
868
- set2 = Functions::string( set2 )
869
- end
870
- else
871
- set1 = Functions::number( set1 )
872
- set2 = Functions::number( set2 )
873
- end
902
+ a = Functions.string(a) unless a_type == :string
903
+ b = Functions.string(b) unless b_type == :string
874
904
  end
875
- return compare( set1, op, set2 )
905
+ when :lt, :lteq, :gt, :gteq
906
+ a = Functions.number(a) unless a_type == :number
907
+ b = Functions.number(b) unless b_type == :number
908
+ else
909
+ message = "[BUG] Unexpected compare operator: " +
910
+ "<#{operator.inspect}>: <#{a.inspect}>: <#{b.inspect}>"
911
+ raise message
876
912
  end
877
- return false
913
+ [a, b]
878
914
  end
879
915
 
880
- def compare a, op, b
881
- case op
916
+ def compare(a, operator, b)
917
+ a, b = normalize_compare_values(a, operator, b)
918
+ case operator
882
919
  when :eq
883
920
  a == b
884
921
  when :neq
@@ -891,22 +928,27 @@ module REXML
891
928
  a > b
892
929
  when :gteq
893
930
  a >= b
894
- when :and
895
- a and b
896
- when :or
897
- a or b
898
931
  else
899
- false
932
+ message = "[BUG] Unexpected compare operator: " +
933
+ "<#{operator.inspect}>: <#{a.inspect}>: <#{b.inspect}>"
934
+ raise message
900
935
  end
901
936
  end
902
937
 
903
- def unnode(nodeset)
904
- nodeset.collect do |node|
938
+ def each_unnode(nodeset)
939
+ return to_enum(__method__, nodeset) unless block_given?
940
+ nodeset.each do |node|
905
941
  if node.is_a?(XPathNode)
906
942
  unnoded = node.raw_node
907
943
  else
908
944
  unnoded = node
909
945
  end
946
+ yield(unnoded)
947
+ end
948
+ end
949
+
950
+ def unnode(nodeset)
951
+ each_unnode(nodeset).collect do |unnoded|
910
952
  unnoded = yield(unnoded) if block_given?
911
953
  unnoded
912
954
  end