summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore10
-rw-r--r--.travis.yml6
-rw-r--r--CHANGELOG.rdoc347
-rw-r--r--Manifest.txt36
-rw-r--r--README.rdoc25
-rw-r--r--Rakefile7
-rw-r--r--ext/psych/.gitignore11
-rw-r--r--ext/psych/depend3
-rw-r--r--ext/psych/extconf.rb38
-rw-r--r--ext/psych/psych.c2
-rw-r--r--ext/psych/psych.h8
-rw-r--r--ext/psych/psych_emitter.c (renamed from ext/psych/emitter.c)85
-rw-r--r--ext/psych/psych_emitter.h (renamed from ext/psych/emitter.h)2
-rw-r--r--ext/psych/psych_parser.c (renamed from ext/psych/parser.c)32
-rw-r--r--ext/psych/psych_parser.h (renamed from ext/psych/parser.h)2
-rw-r--r--ext/psych/psych_to_ruby.c (renamed from ext/psych/to_ruby.c)4
-rw-r--r--ext/psych/psych_to_ruby.h (renamed from ext/psych/to_ruby.h)0
-rw-r--r--ext/psych/psych_yaml_tree.c (renamed from ext/psych/yaml_tree.c)0
-rw-r--r--ext/psych/psych_yaml_tree.h (renamed from ext/psych/yaml_tree.h)0
-rw-r--r--ext/psych/yaml/LICENSE19
-rw-r--r--ext/psych/yaml/api.c1415
-rw-r--r--ext/psych/yaml/config.h10
-rw-r--r--ext/psych/yaml/dumper.c394
-rw-r--r--ext/psych/yaml/emitter.c2329
-rw-r--r--ext/psych/yaml/loader.c459
-rw-r--r--ext/psych/yaml/parser.c1370
-rw-r--r--ext/psych/yaml/reader.c469
-rw-r--r--ext/psych/yaml/scanner.c3583
-rw-r--r--ext/psych/yaml/writer.c141
-rw-r--r--ext/psych/yaml/yaml.h1971
-rw-r--r--ext/psych/yaml/yaml_private.h664
-rw-r--r--lib/psych.rb275
-rw-r--r--lib/psych/class_loader.rb101
-rw-r--r--lib/psych/core_ext.rb9
-rw-r--r--lib/psych/deprecated.rb4
-rw-r--r--lib/psych/exception.rb13
-rw-r--r--lib/psych/handler.rb28
-rw-r--r--lib/psych/handlers/recorder.rb39
-rw-r--r--lib/psych/json/stream.rb1
-rw-r--r--lib/psych/json/yaml_events.rb4
-rw-r--r--lib/psych/nodes/node.rb4
-rw-r--r--lib/psych/nodes/sequence.rb2
-rw-r--r--lib/psych/scalar_scanner.rb77
-rw-r--r--lib/psych/stream.rb1
-rw-r--r--lib/psych/streaming.rb15
-rw-r--r--lib/psych/syntax_error.rb4
-rw-r--r--lib/psych/visitors/emitter.rb15
-rw-r--r--lib/psych/visitors/json_tree.rb7
-rw-r--r--lib/psych/visitors/to_ruby.rb254
-rw-r--r--lib/psych/visitors/yaml_tree.rb212
-rw-r--r--lib/psych/y.rb9
-rw-r--r--test/psych/.swpbin12288 -> 0 bytes
-rw-r--r--test/psych/handlers/test_recorder.rb25
-rw-r--r--test/psych/helper.rb60
-rw-r--r--test/psych/json/test_stream.rb2
-rw-r--r--test/psych/test_alias_and_anchor.rb72
-rw-r--r--test/psych/test_array.rb12
-rw-r--r--test/psych/test_boolean.rb2
-rw-r--r--test/psych/test_class.rb2
-rw-r--r--test/psych/test_coder.rb6
-rw-r--r--test/psych/test_date_time.rb23
-rw-r--r--test/psych/test_deprecated.rb8
-rw-r--r--test/psych/test_document.rb2
-rw-r--r--test/psych/test_emitter.rb5
-rw-r--r--test/psych/test_encoding.rb121
-rw-r--r--test/psych/test_engine_manager.rb57
-rw-r--r--test/psych/test_exception.rb65
-rw-r--r--test/psych/test_hash.rb51
-rw-r--r--test/psych/test_json_tree.rb4
-rw-r--r--test/psych/test_marshalable.rb54
-rw-r--r--test/psych/test_merge_keys.rb101
-rw-r--r--test/psych/test_nil.rb2
-rw-r--r--test/psych/test_null.rb2
-rw-r--r--test/psych/test_numeric.rb22
-rw-r--r--test/psych/test_object.rb2
-rw-r--r--test/psych/test_object_references.rb10
-rw-r--r--test/psych/test_omap.rb9
-rw-r--r--test/psych/test_parser.rb2
-rw-r--r--test/psych/test_psych.rb30
-rw-r--r--test/psych/test_safe_load.rb97
-rw-r--r--test/psych/test_scalar.rb2
-rw-r--r--test/psych/test_scalar_scanner.rb19
-rw-r--r--test/psych/test_serialize_subclasses.rb2
-rw-r--r--test/psych/test_set.rb2
-rw-r--r--test/psych/test_stream.rb2
-rw-r--r--test/psych/test_string.rb92
-rw-r--r--test/psych/test_struct.rb2
-rw-r--r--test/psych/test_symbol.rb10
-rw-r--r--test/psych/test_tainted.rb16
-rw-r--r--test/psych/test_to_yaml_properties.rb2
-rw-r--r--test/psych/test_tree_builder.rb2
-rw-r--r--test/psych/test_yaml.rb27
-rw-r--r--test/psych/test_yamldbm.rb6
-rw-r--r--test/psych/test_yamlstore.rb4
-rw-r--r--test/psych/visitors/test_to_ruby.rb9
-rw-r--r--test/psych/visitors/test_yaml_tree.rb20
96 files changed, 15063 insertions, 518 deletions
diff --git a/.gitignore b/.gitignore
index 6f050e2..40f53fa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,6 @@
-lib/psych/psych.bundle
-lib/psych/psych.jar
-tmp
-pkg
+*.swp
+*.bundle
+*.jar
+*.class
+/pkg
+/tmp
diff --git a/.travis.yml b/.travis.yml
index 9dc61c1..2c45195 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,9 +1,11 @@
rvm:
- 1.9.2
- 1.9.3
+ - 2.0.0
+ - 2.1.0
- ruby-head
before_script:
- - gem install isolate
- gem install hoe
- gem install rake-compiler
-script: rake isolate test
+ - gem install minitest -v '~> 4.0'
+script: rake test
diff --git a/CHANGELOG.rdoc b/CHANGELOG.rdoc
index c48b7aa..4c04f1a 100644
--- a/CHANGELOG.rdoc
+++ b/CHANGELOG.rdoc
@@ -1,3 +1,350 @@
+Fri Jan 9 07:13:55 2015 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/psych/lib/psych/visitors/to_ruby.rb: call `allocate` on hash
+ subclasses. Fixes github.com/tenderlove/psych/issues/196
+
+ * test/psych/test_hash.rb: test for change
+
+Fri Jan 9 06:58:43 2015 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/psych/lib/psych/visitors/to_ruby.rb: revive hashes with ivars
+
+ * ext/psych/lib/psych/visitors/yaml_tree.rb: dump hashes with ivars.
+ Fixes github.com/psych/issues/43
+
+ * test/psych/test_hash.rb: test for change
+
+Sun Nov 23 13:11:24 2014 Sean Griffin <sean@thoughtbot.com>
+
+ * lib/psych/visitors/to_ruby.rb: Allow loading any BasicObject that
+ defines #marshal_load, fixes #100
+ * lib/psych/visitors/yaml_tree.rb: Allow dumping any BasicObject that
+ defines #marshal_dump
+
+Sat Aug 30 06:39:48 2014 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/psych/lib/psych/visitors/yaml_tree.rb: fix NameError dumping and
+ loading. Fixes GH #85. Thanks @brentdax for the patch!
+ * test/psych/test_exception.rb: test for fix
+
+Sat Aug 30 06:23:40 2014 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/psych/lib/psych/scalar_scanner.rb: fix loading strings that
+ look like integers but have a newline. Fixes GH #189
+ * test/psych/test_string.rb: test for fix
+
+Sat Aug 30 06:10:39 2014 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/psych/lib/psych/visitors/to_ruby.rb: merge keys with a hash
+ should merge the hash in to the parent.
+ * test/psych/test_merge_keys.rb: test for change. Fixes GH #202
+
+Sat Aug 30 06:00:26 2014 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/psych/lib/psych/visitors/to_ruby.rb: quoted "<<" strings
+ should not be treated as merge keys.
+ * ext/psych/lib/psych/visitors/yaml_tree.rb: hashes with keys
+ containing "<<" should roundtrip.
+ * test/psych/test_merge_keys.rb: test for change. Fixes GH #203
+
+Wed Aug 6 03:41:21 2014 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/psych/lib/psych/visitors/to_ruby.rb: backwards compatibility for
+ hashes emitted by Syck. Github #198
+ * test/psych/test_hash.rb: test for change.
+
+Fri Jun 6 07:41:41 2014 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/psych/lib/psych/visitors/yaml_tree.rb: dump empty symbols with a
+ tag so that they can be parsed on input. [Bug #9873] [ruby-core:62825]
+ * test/psych/test_symbol.rb: test for change
+
+Sun May 25 11:35:41 2014 Zachary Scott <e@zzak.io>
+
+ * test/psych/*: YAML::ENGINE was removed in [Bug #8344]
+
+2014-03-27 SHIBATA Hiroshi <shibata.hiroshi@gmail.com>
+
+ * ext/psych/yaml/scanner.c: merge libyaml 0.1.6
+ * ext/psych/yaml/yaml_private.h: ditto
+
+Sat Mar 1 11:08:00 2014 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/psych/lib/psych/visitors/yaml_tree.rb: support dumping Encoding
+ objects.
+
+ * ext/psych/lib/psych/visitors/to_ruby.rb: support loading Encoding
+ objects.
+
+ * test/psych/test_encoding.rb: add test
+
+ * ext/psych/lib/psych.rb: add version
+
+Wed Feb 5 10:11:36 2014 Zachary Scott <e@zzak.io>
+
+ * ext/psych/yaml/config.h: bump libyaml to 0.1.5
+
+Wed Feb 5 04:16:41 2014 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/psych/yaml/emitter.c: merge libyaml 0.1.5
+ * ext/psych/yaml/loader.c: ditto
+ * ext/psych/yaml/parser.c: ditto
+ * ext/psych/yaml/reader.c: ditto
+ * ext/psych/yaml/scanner.c: ditto
+ * ext/psych/yaml/writer.c: ditto
+ * ext/psych/yaml/yaml_private.h: ditto
+
+Thu Jan 9 09:55:20 2014 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/psych/lib/psych/visitors/yaml_tree.rb: dumping strings with
+ quotes should not have changed. [ruby-core:59316] [Bug #9300]
+
+ * ext/psych/lib/psych.rb: fixed missing require.
+
+ * test/psych/test_string.rb: test
+
+Wed Nov 27 06:40:18 2013 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/psych/lib/psych/scalar_scanner.rb: fix support for negative
+ years.
+ * ext/psych/lib/psych/visitors/yaml_tree.rb: ditto
+ * test/psych/test_date_time.rb: test for change.
+ Fixes: https://github.com/tenderlove/psych/issues/168
+
+Wed Nov 27 04:46:55 2013 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/psych/lib/psych/scalar_scanner.rb: fix regexp for matching TIME
+ strings.
+ * test/psych/test_date_time.rb: test for change.
+ Fixes: https://github.com/tenderlove/psych/issues/171
+
+Wed Nov 6 04:14:25 2013 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/psych/lib/psych/visitors/to_ruby.rb: process merge keys before
+ reviving objects. Fixes GH psych #168
+ * test/psych/test_merge_keys.rb: test for change
+ https://github.com/tenderlove/psych/issues/168
+
+Wed Oct 30 03:25:10 2013 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/psych/lib/psych/visitors/yaml_tree.rb: make less garbage when
+ testing if a string is binary.
+
+Wed Oct 30 03:08:24 2013 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/psych/lib/psych/visitors/yaml_tree.rb: string subclasses should
+ not be considered to be binary. Fixes Psych / GH 166
+ https://github.com/tenderlove/psych/issues/166
+
+ * test/psych/test_string.rb: test for fix
+
+Fri Sep 20 23:44:07 2013 Zachary Scott <e@zzak.io>
+
+ * ext/psych/yaml/yaml.h: [DOC] fix typo by @GreenGeorge [Fixes GH-161]
+ https://github.com/tenderlove/psych/pull/161
+
+Fri Sep 6 02:37:22 2013 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/psych/lib/psych/visitors/yaml_tree.rb: use double quotes when
+ strings start with special characters.
+ [Fixes GH-157] https://github.com/tenderlove/psych/issues/157
+
+ * test/psych/test_string.rb: test for change.
+
+Thu Aug 29 02:40:45 2013 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/psych/lib/psych/scalar_scanner.rb: invalid floats should be
+ treated as strings.
+ [Fixes GH-156] https://github.com/tenderlove/psych/issues/156
+
+ * test/psych/test_string.rb: test for change
+
+Sat Jul 6 04:49:38 2013 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/psych/lib/psych/visitors/yaml_tree.rb: register time objects so
+ they are referenced as ids during output.
+ * test/psych/test_date_time.rb: corresponding test.
+
+Wed May 15 02:22:16 2013 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/psych/lib/psych.rb: Adding Psych.safe_load for loading a user
+ defined, restricted subset of Ruby object types.
+ * ext/psych/lib/psych/class_loader.rb: A class loader for
+ encapsulating the logic for which objects are allowed to be
+ deserialized.
+ * ext/psych/lib/psych/deprecated.rb: Changes to use the class loader
+ * ext/psych/lib/psych/exception.rb: ditto
+ * ext/psych/lib/psych/json/stream.rb: ditto
+ * ext/psych/lib/psych/nodes/node.rb: ditto
+ * ext/psych/lib/psych/scalar_scanner.rb: ditto
+ * ext/psych/lib/psych/stream.rb: ditto
+ * ext/psych/lib/psych/streaming.rb: ditto
+ * ext/psych/lib/psych/visitors/json_tree.rb: ditto
+ * ext/psych/lib/psych/visitors/to_ruby.rb: ditto
+ * ext/psych/lib/psych/visitors/yaml_tree.rb: ditto
+ * ext/psych/psych_to_ruby.c: ditto
+ * test/psych/helper.rb: ditto
+ * test/psych/test_safe_load.rb: tests for restricted subset.
+ * test/psych/test_scalar_scanner.rb: ditto
+ * test/psych/visitors/test_to_ruby.rb: ditto
+ * test/psych/visitors/test_yaml_tree.rb: ditto
+
+Sat Apr 6 02:54:08 2013 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/psych/lib/psych/exception.rb: there should be only one exception
+ base class. Fixes tenderlove/psych #125
+ * ext/psych/lib/psych.rb: require the correct exception class
+ * ext/psych/lib/psych/syntax_error.rb: ditto
+ * ext/psych/lib/psych/visitors/to_ruby.rb: ditto
+
+Sat Apr 6 02:06:04 2013 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/psych/lib/psych/visitors/to_ruby.rb: correctly register
+ self-referential strings. Fixes tenderlove/psych #135
+
+ * test/psych/test_string.rb: appropriate test.
+
+Fri Mar 1 09:15:00 2013 Zachary Scott <zachary@zacharyscott.net>
+
+ * lib/psych.rb: specify in rdoc what object is returned in parser
+ By Adam Stankiewicz [Github Fixes #133]
+
+Fri Mar 1 03:22:00 2013 Zachary Scott <zachary@zacharyscott.net>
+
+ * lib/psych.rb: rdoc for Psych overview by Adam Stankiewicz
+ [Github Fixes #134]
+
+Sun Feb 17 01:13:00 2013 Zachary Scott <zachary@zacharyscott.net>
+
+ * lib/psych/y.rb: Document Kernel#y by Adam Stankiewicz
+ [Github Fixes #127]
+
+Fri Feb 8 08:53:27 2013 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/psych/lib/psych/visitors/yaml_tree.rb: fixing string quotation
+ when dumping Ruby strings. Thanks Ingy
+
+ * test/psych/test_psych.rb: appropriate tests.
+
+ * test/psych/test_yaml.rb: ditto
+
+Fri Feb 8 08:50:42 2013 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/psych/lib/psych/visitors/yaml_tree.rb: change output reference
+ ids to be sequential numbers.
+
+Thu Jan 17 10:48:56 2013 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/psych/lib/psych/scalar_scanner.rb: use constants rather than
+ calculating Inf and NaN.
+
+Sun Jan 13 16:40:00 2013 Zachary Scott <zachary@zacharyscott.net>
+
+ * ext/psych/yaml/scanner.c: Typos by James Dabbs [Github Fixes #118]
+
+Sat Jan 12 08:58:47 2013 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/psych/lib/psych/visitors/to_ruby.rb: merge key values that
+ contain something besides a hash should be left in tact.
+
+ * test/psych/test_merge_keys.rb: test for change
+
+Thu Jan 10 04:23:07 2013 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/psych/lib/psych/scalar_scanner.rb: strip trailing dots from
+ floats so that Float() will not raise an exception.
+
+ * test/psych/test_numeric.rb: test to ensure "1." can be loaded
+
+ * test/psych/test_string.rb: make sure "1." can round trip
+
+Sat Nov 17 12:03:41 2012 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/psych/lib/psych/scalar_scanner.rb: avoid raising exceptions when
+ parsing Floats and Integers. Thanks riffraff [ruby-core:44426]
+ * test/psych/test_numeric.rb: associated test
+
+Sat Nov 17 11:26:36 2012 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/psych/lib/psych/core_ext.rb: move Kernel#y so that it can
+ manually be required as 'psych/y'.
+
+ * ext/psych/lib/psych/y.rb: ditto
+
+Tue Nov 6 09:37:57 2012 NARUSE, Yui <naruse@ruby-lang.org>
+
+ * ruby.c (load_file_internal): set default source encoding as
+ UTF-8 instead of US-ASCII. [ruby-core:46021] [Feature #6679]
+
+ * parse.y (parser_initialize): set default parser encoding as
+ UTF-8 instead of US-ASCII.
+
+Mon Oct 29 10:22:00 2012 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/psych/lib/psych/handlers/recorder.rb: added a class for
+ recording YAML parse and emit events.
+
+ * ext/psych/lib/psych/handler.rb: adding a list of events so that
+ handler classes can more easily be meta-programmed.
+
+ * test/psych/handlers/test_recorder.rb: tests for the change.
+
+Sun Oct 28 10:12:15 2012 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/psych/lib/psych/visitors/yaml_tree.rb: `tree` should return the
+ same thing on every call.
+
+ * test/psych/visitors/test_yaml_tree.rb: related test.
+
+Sun Oct 28 10:05:03 2012 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/psych/lib/psych/visitors/yaml_tree.rb: YAML Tree object should
+ be able to take an emitter object as it's output.
+
+ * test/psych/visitors/test_yaml_tree.rb: related test.
+
+Thu Jul 19 09:33:46 2012 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/psych/emitter.c (initialize): allow a configuration object to be
+ passed to the constructor so that mutation isn't required after
+ instantiation.
+
+ * ext/psych/lib/psych/handler.rb: add configuration object
+
+ * ext/psych/lib/psych/visitors/emitter.rb: use configuration object if
+ extra configuration is present.
+
+Fri May 18 01:28:21 2012 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/psych/parser.c (transcode_string): fix encoding index names.
+ Thanks markizko for reporting.
+
+Wed May 16 05:11:29 2012 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/psych/lib/psych/visitors/to_ruby.rb: fix a bug with string
+ subclass dumping and loading.
+
+ * test/psych/test_array.rb: pertinent tests
+
+ * test/psych/test_string.rb: ditto
+
+Wed May 16 01:31:21 2012 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/psych/lib/psych/visitors/to_ruby.rb: convert omap tagged maps to
+ Psych::Omap objects rather than hashes. [Bug #6425]
+
+ * test/psych/test_omap.rb: pertinent test.
+
+Wed May 16 01:15:45 2012 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/psych/lib/psych/visitors/yaml_tree.rb: keep a reference to
+ custom coders so that GC does not impact dumped yaml reference ids.
+
+Mon Apr 30 04:43:53 2012 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * ext/psych/lib/psych/json/yaml_events.rb: implicit styles should not
+ be changeable for JSON events.
+
Sat Apr 7 02:07:00 2012 Aaron Patterson <aaron@tenderlovemaking.com>
* ext/psych/parser.c: fall back to any encoding if the external
diff --git a/Manifest.txt b/Manifest.txt
index e7c60c6..264b848 100644
--- a/Manifest.txt
+++ b/Manifest.txt
@@ -4,23 +4,39 @@ CHANGELOG.rdoc
Manifest.txt
README.rdoc
Rakefile
-ext/psych/emitter.c
-ext/psych/emitter.h
+ext/psych/depend
ext/psych/extconf.rb
-ext/psych/parser.c
-ext/psych/parser.h
ext/psych/psych.c
ext/psych/psych.h
-ext/psych/to_ruby.c
-ext/psych/to_ruby.h
-ext/psych/yaml_tree.c
-ext/psych/yaml_tree.h
+ext/psych/psych_emitter.c
+ext/psych/psych_emitter.h
+ext/psych/psych_parser.c
+ext/psych/psych_parser.h
+ext/psych/psych_to_ruby.c
+ext/psych/psych_to_ruby.h
+ext/psych/psych_yaml_tree.c
+ext/psych/psych_yaml_tree.h
+ext/psych/yaml/LICENSE
+ext/psych/yaml/api.c
+ext/psych/yaml/config.h
+ext/psych/yaml/dumper.c
+ext/psych/yaml/emitter.c
+ext/psych/yaml/loader.c
+ext/psych/yaml/parser.c
+ext/psych/yaml/reader.c
+ext/psych/yaml/scanner.c
+ext/psych/yaml/writer.c
+ext/psych/yaml/yaml.h
+ext/psych/yaml/yaml_private.h
lib/psych.rb
+lib/psych/class_loader.rb
lib/psych/coder.rb
lib/psych/core_ext.rb
lib/psych/deprecated.rb
+lib/psych/exception.rb
lib/psych/handler.rb
lib/psych/handlers/document_stream.rb
+lib/psych/handlers/recorder.rb
lib/psych/json/ruby_events.rb
lib/psych/json/stream.rb
lib/psych/json/tree_builder.rb
@@ -48,6 +64,8 @@ lib/psych/visitors/json_tree.rb
lib/psych/visitors/to_ruby.rb
lib/psych/visitors/visitor.rb
lib/psych/visitors/yaml_tree.rb
+lib/psych/y.rb
+test/psych/handlers/test_recorder.rb
test/psych/helper.rb
test/psych/json/test_stream.rb
test/psych/nodes/test_enumerable.rb
@@ -61,7 +79,6 @@ test/psych/test_deprecated.rb
test/psych/test_document.rb
test/psych/test_emitter.rb
test/psych/test_encoding.rb
-test/psych/test_engine_manager.rb
test/psych/test_exception.rb
test/psych/test_hash.rb
test/psych/test_json_tree.rb
@@ -74,6 +91,7 @@ test/psych/test_object_references.rb
test/psych/test_omap.rb
test/psych/test_parser.rb
test/psych/test_psych.rb
+test/psych/test_safe_load.rb
test/psych/test_scalar.rb
test/psych/test_scalar_scanner.rb
test/psych/test_serialize_subclasses.rb
diff --git a/README.rdoc b/README.rdoc
index 0294e31..47ff958 100644
--- a/README.rdoc
+++ b/README.rdoc
@@ -23,8 +23,29 @@ to and from the YAML format.
== Installation
-* sudo port install libyaml +universal
-* sudo install_ruby.sh
+Psych has been included with MRI since 1.9.2,
+and is the default YAML parser in 1.9.3.
+
+If you want a newer gem release of Psych, you can use rubygems:
+
+ gem install psych
+
+In order to use the gem release in your app,
+and not the stdlib version, you'll need the following:
+
+ gem 'psych'
+ require 'psych'
+
+Or if you use Bundler add this to your +Gemfile+:
+
+ gem 'psych'
+
+JRuby ships with a pure Java implementation of Psych.
+
+If you're on Rubinius, Psych is available in 1.9 mode, please refer to the
+Language Modes section of the {Rubinius
+README}[https://github.com/rubinius/rubinius#readme] for more information on
+building and 1.9 mode.
== License
diff --git a/Rakefile b/Rakefile
index c67cb89..3acf614 100644
--- a/Rakefile
+++ b/Rakefile
@@ -16,11 +16,13 @@ class Hoe
end
gem 'rake-compiler', '>= 0.4.1'
+gem 'minitest', '~> 4.0'
require "rake/extensiontask"
-Hoe.plugin :doofus, :git, :gemspec, :isolate
+Hoe.plugin :doofus, :git, :gemspec
$hoe = Hoe.spec 'psych' do
+ license 'MIT'
developer 'Aaron Patterson', 'aaron@tenderlovemaking.com'
self.extra_rdoc_files = Dir['*.rdoc']
@@ -29,6 +31,7 @@ $hoe = Hoe.spec 'psych' do
self.testlib = :minitest
extra_dev_deps << ['rake-compiler', '>= 0.4.1']
+ extra_dev_deps << ['minitest', '~> 4.0']
self.spec_extras = {
:extensions => ["ext/psych/extconf.rb"],
@@ -71,7 +74,7 @@ namespace :merge do
[basedir, 'test', 'psych/'] => [rubydir, 'test', 'psych/'],
}
- rsync = 'rsync -av --exclude extconf.rb --exclude lib --exclude ".*" --exclude "*.o" --exclude Makefile --exclude mkmf.log --delete'
+ rsync = 'rsync -av --exclude lib --exclude ".*" --exclude "*.o" --exclude Makefile --exclude mkmf.log --delete'
task :to_ruby do
mergedirs.each do |from, to|
diff --git a/ext/psych/.gitignore b/ext/psych/.gitignore
new file mode 100644
index 0000000..836058c
--- /dev/null
+++ b/ext/psych/.gitignore
@@ -0,0 +1,11 @@
+/api.c
+/config.h
+/dumper.c
+/emitter.c
+/loader.c
+/parser.c
+/reader.c
+/scanner.c
+/writer.c
+/yaml.h
+/yaml_private.h
diff --git a/ext/psych/depend b/ext/psych/depend
new file mode 100644
index 0000000..79b6c53
--- /dev/null
+++ b/ext/psych/depend
@@ -0,0 +1,3 @@
+$(OBJS): $(HDRS) $(ruby_headers) \
+ $(hdrdir)/ruby/encoding.h \
+ $(hdrdir)/ruby/oniguruma.h
diff --git a/ext/psych/extconf.rb b/ext/psych/extconf.rb
index fe7795f..65e83a3 100644
--- a/ext/psych/extconf.rb
+++ b/ext/psych/extconf.rb
@@ -1,21 +1,37 @@
+# -*- coding: us-ascii -*-
require 'mkmf'
+require 'fileutils'
# :stopdoc:
-RbConfig::MAKEFILE_CONFIG['CC'] = ENV['CC'] if ENV['CC']
+dir_config 'libyaml'
-INCLUDEDIR = Config::CONFIG['includedir']
-LIBDIR = Config::CONFIG['libdir']
-LIB_DIRS = ['/opt/local/lib', '/usr/local/lib', LIBDIR, '/usr/lib']
-libyaml = dir_config 'libyaml', '/opt/local/include', '/opt/local/lib'
+if enable_config("bundled-libyaml", false) || !(find_header('yaml.h') && find_library('yaml', 'yaml_get_version'))
+ # Embed libyaml since we could not find it.
-def asplode missing
- abort "#{missing} is missing. Try 'port install libyaml +universal' " +
- "or 'yum install libyaml-devel'"
-end
+ $VPATH << "$(srcdir)/yaml"
+ $INCFLAGS << " -I$(srcdir)/yaml"
+
+ $srcs = Dir.glob("#{$srcdir}/{,yaml/}*.c").map {|n| File.basename(n)}
+
+ if have_macro("_WIN32")
+ $CPPFLAGS << " -DYAML_DECLARE_STATIC -DHAVE_CONFIG_H"
+ end
-asplode('yaml.h') unless find_header 'yaml.h'
-asplode('libyaml') unless find_library 'yaml', 'yaml_get_version'
+ have_header 'dlfcn.h'
+ have_header 'inttypes.h'
+ have_header 'memory.h'
+ have_header 'stdint.h'
+ have_header 'stdlib.h'
+ have_header 'strings.h'
+ have_header 'string.h'
+ have_header 'sys/stat.h'
+ have_header 'sys/types.h'
+ have_header 'unistd.h'
+
+ find_header 'yaml.h'
+ have_header 'config.h'
+end
create_makefile 'psych'
diff --git a/ext/psych/psych.c b/ext/psych/psych.c
index 69ff1d8..3bb59bf 100644
--- a/ext/psych/psych.c
+++ b/ext/psych/psych.c
@@ -20,7 +20,7 @@ static VALUE libyaml_version(VALUE module)
VALUE mPsych;
-void Init_psych()
+void Init_psych(void)
{
mPsych = rb_define_module("Psych");
diff --git a/ext/psych/psych.h b/ext/psych/psych.h
index 9f1be44..1830ca4 100644
--- a/ext/psych/psych.h
+++ b/ext/psych/psych.h
@@ -9,10 +9,10 @@
#include <yaml.h>
-#include <parser.h>
-#include <emitter.h>
-#include <to_ruby.h>
-#include <yaml_tree.h>
+#include <psych_parser.h>
+#include <psych_emitter.h>
+#include <psych_to_ruby.h>
+#include <psych_yaml_tree.h>
extern VALUE mPsych;
diff --git a/ext/psych/emitter.c b/ext/psych/psych_emitter.c
index 15fdcfe..4ba2381 100644
--- a/ext/psych/emitter.c
+++ b/ext/psych/psych_emitter.c
@@ -2,6 +2,9 @@
VALUE cPsychEmitter;
static ID id_write;
+static ID id_line_width;
+static ID id_indentation;
+static ID id_canonical;
static void emit(yaml_emitter_t * emitter, yaml_event_t * event)
{
@@ -26,6 +29,24 @@ static void dealloc(void * ptr)
xfree(emitter);
}
+#if 0
+static size_t memsize(const void *ptr)
+{
+ const yaml_emitter_t *emitter = ptr;
+ /* TODO: calculate emitter's size */
+ return 0;
+}
+#endif
+
+static const rb_data_type_t psych_emitter_type = {
+ "Psych/emitter",
+ {0, dealloc, 0,},
+ 0, 0,
+#ifdef RUBY_TYPED_FREE_IMMEDIATELY
+ RUBY_TYPED_FREE_IMMEDIATELY,
+#endif
+};
+
static VALUE allocate(VALUE klass)
{
yaml_emitter_t * emitter;
@@ -36,17 +57,32 @@ static VALUE allocate(VALUE klass)
yaml_emitter_set_unicode(emitter, 1);
yaml_emitter_set_indent(emitter, 2);
- return Data_Wrap_Struct(klass, 0, dealloc, emitter);
+ return TypedData_Wrap_Struct(klass, &psych_emitter_type, emitter);
}
-/* call-seq: Psych::Emitter.new(io)
+/* call-seq: Psych::Emitter.new(io, options = Psych::Emitter::OPTIONS)
*
* Create a new Psych::Emitter that writes to +io+.
*/
-static VALUE initialize(VALUE self, VALUE io)
+static VALUE initialize(int argc, VALUE *argv, VALUE self)
{
yaml_emitter_t * emitter;
- Data_Get_Struct(self, yaml_emitter_t, emitter);
+ VALUE io, options;
+ VALUE line_width;
+ VALUE indent;
+ VALUE canonical;
+
+ TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter);
+
+ if (rb_scan_args(argc, argv, "11", &io, &options) == 2) {
+ line_width = rb_funcall(options, id_line_width, 0);
+ indent = rb_funcall(options, id_indentation, 0);
+ canonical = rb_funcall(options, id_canonical, 0);
+
+ yaml_emitter_set_width(emitter, NUM2INT(line_width));
+ yaml_emitter_set_indent(emitter, NUM2INT(indent));
+ yaml_emitter_set_canonical(emitter, Qtrue == canonical ? 1 : 0);
+ }
yaml_emitter_set_output(emitter, writer, (void *)io);
@@ -63,7 +99,7 @@ static VALUE start_stream(VALUE self, VALUE encoding)
{
yaml_emitter_t * emitter;
yaml_event_t event;
- Data_Get_Struct(self, yaml_emitter_t, emitter);
+ TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter);
Check_Type(encoding, T_FIXNUM);
yaml_stream_start_event_initialize(&event, (yaml_encoding_t)NUM2INT(encoding));
@@ -83,7 +119,7 @@ static VALUE end_stream(VALUE self)
{
yaml_emitter_t * emitter;
yaml_event_t event;
- Data_Get_Struct(self, yaml_emitter_t, emitter);
+ TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter);
yaml_stream_end_event_initialize(&event);
@@ -106,7 +142,7 @@ static VALUE start_document(VALUE self, VALUE version, VALUE tags, VALUE imp)
yaml_tag_directive_t * tail = NULL;
yaml_event_t event;
yaml_version_directive_t version_directive;
- Data_Get_Struct(self, yaml_emitter_t, emitter);
+ TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter);
Check_Type(version, T_ARRAY);
@@ -180,7 +216,7 @@ static VALUE end_document(VALUE self, VALUE imp)
{
yaml_emitter_t * emitter;
yaml_event_t event;
- Data_Get_Struct(self, yaml_emitter_t, emitter);
+ TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter);
yaml_document_end_event_initialize(&event, imp ? 1 : 0);
@@ -210,7 +246,7 @@ static VALUE scalar(
#ifdef HAVE_RUBY_ENCODING_H
rb_encoding *encoding;
#endif
- Data_Get_Struct(self, yaml_emitter_t, emitter);
+ TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter);
Check_Type(value, T_STRING);
@@ -277,7 +313,7 @@ static VALUE start_sequence(
}
#endif
- Data_Get_Struct(self, yaml_emitter_t, emitter);
+ TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter);
yaml_sequence_start_event_initialize(
&event,
@@ -302,7 +338,7 @@ static VALUE end_sequence(VALUE self)
{
yaml_emitter_t * emitter;
yaml_event_t event;
- Data_Get_Struct(self, yaml_emitter_t, emitter);
+ TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter);
yaml_sequence_end_event_initialize(&event);
@@ -330,7 +366,7 @@ static VALUE start_mapping(
#ifdef HAVE_RUBY_ENCODING_H
rb_encoding *encoding;
#endif
- Data_Get_Struct(self, yaml_emitter_t, emitter);
+ TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter);
#ifdef HAVE_RUBY_ENCODING_H
encoding = rb_utf8_encoding();
@@ -369,7 +405,7 @@ static VALUE end_mapping(VALUE self)
{
yaml_emitter_t * emitter;
yaml_event_t event;
- Data_Get_Struct(self, yaml_emitter_t, emitter);
+ TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter);
yaml_mapping_end_event_initialize(&event);
@@ -388,7 +424,7 @@ static VALUE alias(VALUE self, VALUE anchor)
{
yaml_emitter_t * emitter;
yaml_event_t event;
- Data_Get_Struct(self, yaml_emitter_t, emitter);
+ TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter);
#ifdef HAVE_RUBY_ENCODING_H
if(!NIL_P(anchor)) {
@@ -414,7 +450,7 @@ static VALUE alias(VALUE self, VALUE anchor)
static VALUE set_canonical(VALUE self, VALUE style)
{
yaml_emitter_t * emitter;
- Data_Get_Struct(self, yaml_emitter_t, emitter);
+ TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter);
yaml_emitter_set_canonical(emitter, Qtrue == style ? 1 : 0);
@@ -428,7 +464,7 @@ static VALUE set_canonical(VALUE self, VALUE style)
static VALUE canonical(VALUE self)
{
yaml_emitter_t * emitter;
- Data_Get_Struct(self, yaml_emitter_t, emitter);
+ TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter);
return (emitter->canonical == 0) ? Qfalse : Qtrue;
}
@@ -441,7 +477,7 @@ static VALUE canonical(VALUE self)
static VALUE set_indentation(VALUE self, VALUE level)
{
yaml_emitter_t * emitter;
- Data_Get_Struct(self, yaml_emitter_t, emitter);
+ TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter);
yaml_emitter_set_indent(emitter, NUM2INT(level));
@@ -455,7 +491,7 @@ static VALUE set_indentation(VALUE self, VALUE level)
static VALUE indentation(VALUE self)
{
yaml_emitter_t * emitter;
- Data_Get_Struct(self, yaml_emitter_t, emitter);
+ TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter);
return INT2NUM(emitter->best_indent);
}
@@ -467,7 +503,7 @@ static VALUE indentation(VALUE self)
static VALUE line_width(VALUE self)
{
yaml_emitter_t * emitter;
- Data_Get_Struct(self, yaml_emitter_t, emitter);
+ TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter);
return INT2NUM(emitter->best_width);
}
@@ -479,14 +515,14 @@ static VALUE line_width(VALUE self)
static VALUE set_line_width(VALUE self, VALUE width)
{
yaml_emitter_t * emitter;
- Data_Get_Struct(self, yaml_emitter_t, emitter);
+ TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter);
yaml_emitter_set_width(emitter, NUM2INT(width));
return width;
}
-void Init_psych_emitter()
+void Init_psych_emitter(void)
{
VALUE psych = rb_define_module("Psych");
VALUE handler = rb_define_class_under(psych, "Handler", rb_cObject);
@@ -494,7 +530,7 @@ void Init_psych_emitter()
rb_define_alloc_func(cPsychEmitter, allocate);
- rb_define_method(cPsychEmitter, "initialize", initialize, 1);
+ rb_define_method(cPsychEmitter, "initialize", initialize, -1);
rb_define_method(cPsychEmitter, "start_stream", start_stream, 1);
rb_define_method(cPsychEmitter, "end_stream", end_stream, 0);
rb_define_method(cPsychEmitter, "start_document", start_document, 3);
@@ -512,6 +548,9 @@ void Init_psych_emitter()
rb_define_method(cPsychEmitter, "line_width", line_width, 0);
rb_define_method(cPsychEmitter, "line_width=", set_line_width, 1);
- id_write = rb_intern("write");
+ id_write = rb_intern("write");
+ id_line_width = rb_intern("line_width");
+ id_indentation = rb_intern("indentation");
+ id_canonical = rb_intern("canonical");
}
/* vim: set noet sws=4 sw=4: */
diff --git a/ext/psych/emitter.h b/ext/psych/psych_emitter.h
index 560451e..4c1482a 100644
--- a/ext/psych/emitter.h
+++ b/ext/psych/psych_emitter.h
@@ -3,6 +3,6 @@
#include <psych.h>
-void Init_psych_emitter();
+void Init_psych_emitter(void);
#endif
diff --git a/ext/psych/parser.c b/ext/psych/psych_parser.c
index 6f4c456..faae460 100644
--- a/ext/psych/parser.c
+++ b/ext/psych/psych_parser.c
@@ -49,6 +49,24 @@ static void dealloc(void * ptr)
xfree(parser);
}
+#if 0
+static size_t memsize(const void *ptr)
+{
+ const yaml_parser_t *parser = ptr;
+ /* TODO: calculate parser's size */
+ return 0;
+}
+#endif
+
+static const rb_data_type_t psych_parser_type = {
+ "Psych/parser",
+ {0, dealloc, 0,},
+ 0, 0,
+#ifdef RUBY_TYPED_FREE_IMMEDIATELY
+ RUBY_TYPED_FREE_IMMEDIATELY,
+#endif
+};
+
static VALUE allocate(VALUE klass)
{
yaml_parser_t * parser;
@@ -56,7 +74,7 @@ static VALUE allocate(VALUE klass)
parser = xmalloc(sizeof(yaml_parser_t));
yaml_parser_initialize(parser);
- return Data_Wrap_Struct(klass, 0, dealloc, parser);
+ return TypedData_Wrap_Struct(klass, &psych_parser_type, parser);
}
static VALUE make_exception(yaml_parser_t * parser, VALUE path)
@@ -79,8 +97,8 @@ static VALUE make_exception(yaml_parser_t * parser, VALUE path)
static VALUE transcode_string(VALUE src, int * parser_encoding)
{
int utf8 = rb_utf8_encindex();
- int utf16le = rb_enc_find_index("UTF16_LE");
- int utf16be = rb_enc_find_index("UTF16_BE");
+ int utf16le = rb_enc_find_index("UTF-16LE");
+ int utf16be = rb_enc_find_index("UTF-16BE");
int source_encoding = rb_enc_get_index(src);
if (source_encoding == utf8) {
@@ -248,7 +266,7 @@ static VALUE parse(int argc, VALUE *argv, VALUE self)
path = rb_str_new2("<unknown>");
}
- Data_Get_Struct(self, yaml_parser_t, parser);
+ TypedData_Get_Struct(self, yaml_parser_t, &psych_parser_type, parser);
yaml_parser_delete(parser);
yaml_parser_initialize(parser);
@@ -526,7 +544,7 @@ static VALUE mark(VALUE self)
VALUE args[3];
yaml_parser_t * parser;
- Data_Get_Struct(self, yaml_parser_t, parser);
+ TypedData_Get_Struct(self, yaml_parser_t, &psych_parser_type, parser);
mark_klass = rb_const_get_at(cPsychParser, rb_intern("Mark"));
args[0] = INT2NUM(parser->mark.index);
args[1] = INT2NUM(parser->mark.line);
@@ -535,7 +553,7 @@ static VALUE mark(VALUE self)
return rb_class_new_instance(3, args, mark_klass);
}
-void Init_psych_parser()
+void Init_psych_parser(void)
{
#if 0
mPsych = rb_define_module("Psych");
@@ -557,7 +575,7 @@ void Init_psych_parser()
rb_define_const(cPsychParser, "UTF16BE", INT2NUM(YAML_UTF16BE_ENCODING));
rb_require("psych/syntax_error");
- ePsychSyntaxError = rb_define_class_under(mPsych, "SyntaxError", rb_eSyntaxError);
+ ePsychSyntaxError = rb_const_get(mPsych, rb_intern("SyntaxError"));
rb_define_method(cPsychParser, "parse", parse, -1);
rb_define_method(cPsychParser, "mark", mark, 0);
diff --git a/ext/psych/parser.h b/ext/psych/psych_parser.h
index 25e896f..beb3dd0 100644
--- a/ext/psych/parser.h
+++ b/ext/psych/psych_parser.h
@@ -1,6 +1,6 @@
#ifndef PSYCH_PARSER_H
#define PSYCH_PARSER_H
-void Init_psych_parser();
+void Init_psych_parser(void);
#endif
diff --git a/ext/psych/to_ruby.c b/ext/psych/psych_to_ruby.c
index ed5245e..3cc87a9 100644
--- a/ext/psych/to_ruby.c
+++ b/ext/psych/psych_to_ruby.c
@@ -31,11 +31,13 @@ static VALUE path2class(VALUE self, VALUE path)
void Init_psych_to_ruby(void)
{
VALUE psych = rb_define_module("Psych");
+ VALUE class_loader = rb_define_class_under(psych, "ClassLoader", rb_cObject);
+
VALUE visitors = rb_define_module_under(psych, "Visitors");
VALUE visitor = rb_define_class_under(visitors, "Visitor", rb_cObject);
cPsychVisitorsToRuby = rb_define_class_under(visitors, "ToRuby", visitor);
rb_define_private_method(cPsychVisitorsToRuby, "build_exception", build_exception, 2);
- rb_define_private_method(cPsychVisitorsToRuby, "path2class", path2class, 1);
+ rb_define_private_method(class_loader, "path2class", path2class, 1);
}
/* vim: set noet sws=4 sw=4: */
diff --git a/ext/psych/to_ruby.h b/ext/psych/psych_to_ruby.h
index 7b8e757..7b8e757 100644
--- a/ext/psych/to_ruby.h
+++ b/ext/psych/psych_to_ruby.h
diff --git a/ext/psych/yaml_tree.c b/ext/psych/psych_yaml_tree.c
index bcf24d2..bcf24d2 100644
--- a/ext/psych/yaml_tree.c
+++ b/ext/psych/psych_yaml_tree.c
diff --git a/ext/psych/yaml_tree.h b/ext/psych/psych_yaml_tree.h
index 4628a69..4628a69 100644
--- a/ext/psych/yaml_tree.h
+++ b/ext/psych/psych_yaml_tree.h
diff --git a/ext/psych/yaml/LICENSE b/ext/psych/yaml/LICENSE
new file mode 100644
index 0000000..050ced2
--- /dev/null
+++ b/ext/psych/yaml/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2006 Kirill Simonov
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/ext/psych/yaml/api.c b/ext/psych/yaml/api.c
new file mode 100644
index 0000000..e0b9d97
--- /dev/null
+++ b/ext/psych/yaml/api.c
@@ -0,0 +1,1415 @@
+
+#include "yaml_private.h"
+
+/*
+ * Get the library version.
+ */
+
+YAML_DECLARE(const char *)
+yaml_get_version_string(void)
+{
+ return YAML_VERSION_STRING;
+}
+
+/*
+ * Get the library version numbers.
+ */
+
+YAML_DECLARE(void)
+yaml_get_version(int *major, int *minor, int *patch)
+{
+ *major = YAML_VERSION_MAJOR;
+ *minor = YAML_VERSION_MINOR;
+ *patch = YAML_VERSION_PATCH;
+}
+
+/*
+ * Allocate a dynamic memory block.
+ */
+
+YAML_DECLARE(void *)
+yaml_malloc(size_t size)
+{
+ return malloc(size ? size : 1);
+}
+
+/*
+ * Reallocate a dynamic memory block.
+ */
+
+YAML_DECLARE(void *)
+yaml_realloc(void *ptr, size_t size)
+{
+ return ptr ? realloc(ptr, size ? size : 1) : malloc(size ? size : 1);
+}
+
+/*
+ * Free a dynamic memory block.
+ */
+
+YAML_DECLARE(void)
+yaml_free(void *ptr)
+{
+ if (ptr) free(ptr);
+}
+
+/*
+ * Duplicate a string.
+ */
+
+YAML_DECLARE(yaml_char_t *)
+yaml_strdup(const yaml_char_t *str)
+{
+ if (!str)
+ return NULL;
+
+ return (yaml_char_t *)strdup((char *)str);
+}
+
+/*
+ * Extend a string.
+ */
+
+YAML_DECLARE(int)
+yaml_string_extend(yaml_char_t **start,
+ yaml_char_t **pointer, yaml_char_t **end)
+{
+ yaml_char_t *new_start = yaml_realloc(*start, (*end - *start)*2);
+
+ if (!new_start) return 0;
+
+ memset(new_start + (*end - *start), 0, *end - *start);
+
+ *pointer = new_start + (*pointer - *start);
+ *end = new_start + (*end - *start)*2;
+ *start = new_start;
+
+ return 1;
+}
+
+/*
+ * Append a string B to a string A.
+ */
+
+YAML_DECLARE(int)
+yaml_string_join(
+ yaml_char_t **a_start, yaml_char_t **a_pointer, yaml_char_t **a_end,
+ yaml_char_t **b_start, yaml_char_t **b_pointer, yaml_char_t **b_end)
+{
+ if (*b_start == *b_pointer)
+ return 1;
+
+ while (*a_end - *a_pointer <= *b_pointer - *b_start) {
+ if (!yaml_string_extend(a_start, a_pointer, a_end))
+ return 0;
+ }
+
+ memcpy(*a_pointer, *b_start, *b_pointer - *b_start);
+ *a_pointer += *b_pointer - *b_start;
+
+ return 1;
+}
+
+/*
+ * Extend a stack.
+ */
+
+YAML_DECLARE(int)
+yaml_stack_extend(void **start, void **top, void **end)
+{
+ void *new_start = yaml_realloc(*start, ((char *)*end - (char *)*start)*2);
+
+ if (!new_start) return 0;
+
+ *top = (char *)new_start + ((char *)*top - (char *)*start);
+ *end = (char *)new_start + ((char *)*end - (char *)*start)*2;
+ *start = new_start;
+
+ return 1;
+}
+
+/*
+ * Extend or move a queue.
+ */
+
+YAML_DECLARE(int)
+yaml_queue_extend(void **start, void **head, void **tail, void **end)
+{
+ /* Check if we need to resize the queue. */
+
+ if (*start == *head && *tail == *end) {
+ void *new_start = yaml_realloc(*start,
+ ((char *)*end - (char *)*start)*2);
+
+ if (!new_start) return 0;
+
+ *head = (char *)new_start + ((char *)*head - (char *)*start);
+ *tail = (char *)new_start + ((char *)*tail - (char *)*start);
+ *end = (char *)new_start + ((char *)*end - (char *)*start)*2;
+ *start = new_start;
+ }
+
+ /* Check if we need to move the queue at the beginning of the buffer. */
+
+ if (*tail == *end) {
+ if (*head != *tail) {
+ memmove(*start, *head, (char *)*tail - (char *)*head);
+ }
+ *tail = (char *)*tail - (char *)*head + (char *)*start;
+ *head = *start;
+ }
+
+ return 1;
+}
+
+
+/*
+ * Create a new parser object.
+ */
+
+YAML_DECLARE(int)
+yaml_parser_initialize(yaml_parser_t *parser)
+{
+ assert(parser); /* Non-NULL parser object expected. */
+
+ memset(parser, 0, sizeof(yaml_parser_t));
+ if (!BUFFER_INIT(parser, parser->raw_buffer, INPUT_RAW_BUFFER_SIZE))
+ goto error;
+ if (!BUFFER_INIT(parser, parser->buffer, INPUT_BUFFER_SIZE))
+ goto error;
+ if (!QUEUE_INIT(parser, parser->tokens, INITIAL_QUEUE_SIZE))
+ goto error;
+ if (!STACK_INIT(parser, parser->indents, INITIAL_STACK_SIZE))
+ goto error;
+ if (!STACK_INIT(parser, parser->simple_keys, INITIAL_STACK_SIZE))
+ goto error;
+ if (!STACK_INIT(parser, parser->states, INITIAL_STACK_SIZE))
+ goto error;
+ if (!STACK_INIT(parser, parser->marks, INITIAL_STACK_SIZE))
+ goto error;
+ if (!STACK_INIT(parser, parser->tag_directives, INITIAL_STACK_SIZE))
+ goto error;
+
+ return 1;
+
+error:
+
+ BUFFER_DEL(parser, parser->raw_buffer);
+ BUFFER_DEL(parser, parser->buffer);
+ QUEUE_DEL(parser, parser->tokens);
+ STACK_DEL(parser, parser->indents);
+ STACK_DEL(parser, parser->simple_keys);
+ STACK_DEL(parser, parser->states);
+ STACK_DEL(parser, parser->marks);
+ STACK_DEL(parser, parser->tag_directives);
+
+ return 0;
+}
+
+/*
+ * Destroy a parser object.
+ */
+
+YAML_DECLARE(void)
+yaml_parser_delete(yaml_parser_t *parser)
+{
+ assert(parser); /* Non-NULL parser object expected. */
+
+ BUFFER_DEL(parser, parser->raw_buffer);
+ BUFFER_DEL(parser, parser->buffer);
+ while (!QUEUE_EMPTY(parser, parser->tokens)) {
+ yaml_token_delete(&DEQUEUE(parser, parser->tokens));
+ }
+ QUEUE_DEL(parser, parser->tokens);
+ STACK_DEL(parser, parser->indents);
+ STACK_DEL(parser, parser->simple_keys);
+ STACK_DEL(parser, parser->states);
+ STACK_DEL(parser, parser->marks);
+ while (!STACK_EMPTY(parser, parser->tag_directives)) {
+ yaml_tag_directive_t tag_directive = POP(parser, parser->tag_directives);
+ yaml_free(tag_directive.handle);
+ yaml_free(tag_directive.prefix);
+ }
+ STACK_DEL(parser, parser->tag_directives);
+
+ memset(parser, 0, sizeof(yaml_parser_t));
+}
+
+/*
+ * String read handler.
+ */
+
+static int
+yaml_string_read_handler(void *data, unsigned char *buffer, size_t size,
+ size_t *size_read)
+{
+ yaml_parser_t *parser = data;
+
+ if (parser->input.string.current == parser->input.string.end) {
+ *size_read = 0;
+ return 1;
+ }
+
+ if (size > (size_t)(parser->input.string.end
+ - parser->input.string.current)) {
+ size = parser->input.string.end - parser->input.string.current;
+ }
+
+ memcpy(buffer, parser->input.string.current, size);
+ parser->input.string.current += size;
+ *size_read = size;
+ return 1;
+}
+
+/*
+ * File read handler.
+ */
+
+static int
+yaml_file_read_handler(void *data, unsigned char *buffer, size_t size,
+ size_t *size_read)
+{
+ yaml_parser_t *parser = data;
+
+ *size_read = fread(buffer, 1, size, parser->input.file);
+ return !ferror(parser->input.file);
+}
+
+/*
+ * Set a string input.
+ */
+
+YAML_DECLARE(void)
+yaml_parser_set_input_string(yaml_parser_t *parser,
+ const unsigned char *input, size_t size)
+{
+ assert(parser); /* Non-NULL parser object expected. */
+ assert(!parser->read_handler); /* You can set the source only once. */
+ assert(input); /* Non-NULL input string expected. */
+
+ parser->read_handler = yaml_string_read_handler;
+ parser->read_handler_data = parser;
+
+ parser->input.string.start = input;
+ parser->input.string.current = input;
+ parser->input.string.end = input+size;
+}
+
+/*
+ * Set a file input.
+ */
+
+YAML_DECLARE(void)
+yaml_parser_set_input_file(yaml_parser_t *parser, FILE *file)
+{
+ assert(parser); /* Non-NULL parser object expected. */
+ assert(!parser->read_handler); /* You can set the source only once. */
+ assert(file); /* Non-NULL file object expected. */
+
+ parser->read_handler = yaml_file_read_handler;
+ parser->read_handler_data = parser;
+
+ parser->input.file = file;
+}
+
+/*
+ * Set a generic input.
+ */
+
+YAML_DECLARE(void)
+yaml_parser_set_input(yaml_parser_t *parser,
+ yaml_read_handler_t *handler, void *data)
+{
+ assert(parser); /* Non-NULL parser object expected. */
+ assert(!parser->read_handler); /* You can set the source only once. */
+ assert(handler); /* Non-NULL read handler expected. */
+
+ parser->read_handler = handler;
+ parser->read_handler_data = data;
+}
+
+/*
+ * Set the source encoding.
+ */
+
+YAML_DECLARE(void)
+yaml_parser_set_encoding(yaml_parser_t *parser, yaml_encoding_t encoding)
+{
+ assert(parser); /* Non-NULL parser object expected. */
+ assert(!parser->encoding); /* Encoding is already set or detected. */
+
+ parser->encoding = encoding;
+}
+
+/*
+ * Create a new emitter object.
+ */
+
+YAML_DECLARE(int)
+yaml_emitter_initialize(yaml_emitter_t *emitter)
+{
+ assert(emitter); /* Non-NULL emitter object expected. */
+
+ memset(emitter, 0, sizeof(yaml_emitter_t));
+ if (!BUFFER_INIT(emitter, emitter->buffer, OUTPUT_BUFFER_SIZE))
+ goto error;
+ if (!BUFFER_INIT(emitter, emitter->raw_buffer, OUTPUT_RAW_BUFFER_SIZE))
+ goto error;
+ if (!STACK_INIT(emitter, emitter->states, INITIAL_STACK_SIZE))
+ goto error;
+ if (!QUEUE_INIT(emitter, emitter->events, INITIAL_QUEUE_SIZE))
+ goto error;
+ if (!STACK_INIT(emitter, emitter->indents, INITIAL_STACK_SIZE))
+ goto error;
+ if (!STACK_INIT(emitter, emitter->tag_directives, INITIAL_STACK_SIZE))
+ goto error;
+
+ return 1;
+
+error:
+
+ BUFFER_DEL(emitter, emitter->buffer);
+ BUFFER_DEL(emitter, emitter->raw_buffer);
+ STACK_DEL(emitter, emitter->states);
+ QUEUE_DEL(emitter, emitter->events);
+ STACK_DEL(emitter, emitter->indents);
+ STACK_DEL(emitter, emitter->tag_directives);
+
+ return 0;
+}
+
+/*
+ * Destroy an emitter object.
+ */
+
+YAML_DECLARE(void)
+yaml_emitter_delete(yaml_emitter_t *emitter)
+{
+ assert(emitter); /* Non-NULL emitter object expected. */
+
+ BUFFER_DEL(emitter, emitter->buffer);
+ BUFFER_DEL(emitter, emitter->raw_buffer);
+ STACK_DEL(emitter, emitter->states);
+ while (!QUEUE_EMPTY(emitter, emitter->events)) {
+ yaml_event_delete(&DEQUEUE(emitter, emitter->events));
+ }
+ QUEUE_DEL(emitter, emitter->events);
+ STACK_DEL(emitter, emitter->indents);
+ while (!STACK_EMPTY(emitter, emitter->tag_directives)) {
+ yaml_tag_directive_t tag_directive = POP(emitter, emitter->tag_directives);
+ yaml_free(tag_directive.handle);
+ yaml_free(tag_directive.prefix);
+ }
+ STACK_DEL(emitter, emitter->tag_directives);
+ yaml_free(emitter->anchors);
+
+ memset(emitter, 0, sizeof(yaml_emitter_t));
+}
+
+/*
+ * String write handler.
+ */
+
+static int
+yaml_string_write_handler(void *data, unsigned char *buffer, size_t size)
+{
+ yaml_emitter_t *emitter = data;
+
+ if (emitter->output.string.size + *emitter->output.string.size_written
+ < size) {
+ memcpy(emitter->output.string.buffer
+ + *emitter->output.string.size_written,
+ buffer,
+ emitter->output.string.size
+ - *emitter->output.string.size_written);
+ *emitter->output.string.size_written = emitter->output.string.size;
+ return 0;
+ }
+
+ memcpy(emitter->output.string.buffer
+ + *emitter->output.string.size_written, buffer, size);
+ *emitter->output.string.size_written += size;
+ return 1;
+}
+
+/*
+ * File write handler.
+ */
+
+static int
+yaml_file_write_handler(void *data, unsigned char *buffer, size_t size)
+{
+ yaml_emitter_t *emitter = data;
+
+ return (fwrite(buffer, 1, size, emitter->output.file) == size);
+}
+/*
+ * Set a string output.
+ */
+
+YAML_DECLARE(void)
+yaml_emitter_set_output_string(yaml_emitter_t *emitter,
+ unsigned char *output, size_t size, size_t *size_written)
+{
+ assert(emitter); /* Non-NULL emitter object expected. */
+ assert(!emitter->write_handler); /* You can set the output only once. */
+ assert(output); /* Non-NULL output string expected. */
+
+ emitter->write_handler = yaml_string_write_handler;
+ emitter->write_handler_data = emitter;
+
+ emitter->output.string.buffer = output;
+ emitter->output.string.size = size;
+ emitter->output.string.size_written = size_written;
+ *size_written = 0;
+}
+
+/*
+ * Set a file output.
+ */
+
+YAML_DECLARE(void)
+yaml_emitter_set_output_file(yaml_emitter_t *emitter, FILE *file)
+{
+ assert(emitter); /* Non-NULL emitter object expected. */
+ assert(!emitter->write_handler); /* You can set the output only once. */
+ assert(file); /* Non-NULL file object expected. */
+
+ emitter->write_handler = yaml_file_write_handler;
+ emitter->write_handler_data = emitter;
+
+ emitter->output.file = file;
+}
+
+/*
+ * Set a generic output handler.
+ */
+
+YAML_DECLARE(void)
+yaml_emitter_set_output(yaml_emitter_t *emitter,
+ yaml_write_handler_t *handler, void *data)
+{
+ assert(emitter); /* Non-NULL emitter object expected. */
+ assert(!emitter->write_handler); /* You can set the output only once. */
+ assert(handler); /* Non-NULL handler object expected. */
+
+ emitter->write_handler = handler;
+ emitter->write_handler_data = data;
+}
+
+/*
+ * Set the output encoding.
+ */
+
+YAML_DECLARE(void)
+yaml_emitter_set_encoding(yaml_emitter_t *emitter, yaml_encoding_t encoding)
+{
+ assert(emitter); /* Non-NULL emitter object expected. */
+ assert(!emitter->encoding); /* You can set encoding only once. */
+
+ emitter->encoding = encoding;
+}
+
+/*
+ * Set the canonical output style.
+ */
+
+YAML_DECLARE(void)
+yaml_emitter_set_canonical(yaml_emitter_t *emitter, int canonical)
+{
+ assert(emitter); /* Non-NULL emitter object expected. */
+
+ emitter->canonical = (canonical != 0);
+}
+
+/*
+ * Set the indentation increment.
+ */
+
+YAML_DECLARE(void)
+yaml_emitter_set_indent(yaml_emitter_t *emitter, int indent)
+{
+ assert(emitter); /* Non-NULL emitter object expected. */
+
+ emitter->best_indent = (1 < indent && indent < 10) ? indent : 2;
+}
+
+/*
+ * Set the preferred line width.
+ */
+
+YAML_DECLARE(void)
+yaml_emitter_set_width(yaml_emitter_t *emitter, int width)
+{
+ assert(emitter); /* Non-NULL emitter object expected. */
+
+ emitter->best_width = (width >= 0) ? width : -1;
+}
+
+/*
+ * Set if unescaped non-ASCII characters are allowed.
+ */
+
+YAML_DECLARE(void)
+yaml_emitter_set_unicode(yaml_emitter_t *emitter, int unicode)
+{
+ assert(emitter); /* Non-NULL emitter object expected. */
+
+ emitter->unicode = (unicode != 0);
+}
+
+/*
+ * Set the preferred line break character.
+ */
+
+YAML_DECLARE(void)
+yaml_emitter_set_break(yaml_emitter_t *emitter, yaml_break_t line_break)
+{
+ assert(emitter); /* Non-NULL emitter object expected. */
+
+ emitter->line_break = line_break;
+}
+
+/*
+ * Destroy a token object.
+ */
+
+YAML_DECLARE(void)
+yaml_token_delete(yaml_token_t *token)
+{
+ assert(token); /* Non-NULL token object expected. */
+
+ switch (token->type)
+ {
+ case YAML_TAG_DIRECTIVE_TOKEN:
+ yaml_free(token->data.tag_directive.handle);
+ yaml_free(token->data.tag_directive.prefix);
+ break;
+
+ case YAML_ALIAS_TOKEN:
+ yaml_free(token->data.alias.value);
+ break;
+
+ case YAML_ANCHOR_TOKEN:
+ yaml_free(token->data.anchor.value);
+ break;
+
+ case YAML_TAG_TOKEN:
+ yaml_free(token->data.tag.handle);
+ yaml_free(token->data.tag.suffix);
+ break;
+
+ case YAML_SCALAR_TOKEN:
+ yaml_free(token->data.scalar.value);
+ break;
+
+ default:
+ break;
+ }
+
+ memset(token, 0, sizeof(yaml_token_t));
+}
+
+/*
+ * Check if a string is a valid UTF-8 sequence.
+ *
+ * Check 'reader.c' for more details on UTF-8 encoding.
+ */
+
+static int
+yaml_check_utf8(yaml_char_t *start, size_t length)
+{
+ yaml_char_t *end = start+length;
+ yaml_char_t *pointer = start;
+
+ while (pointer < end) {
+ unsigned char octet;
+ unsigned int width;
+ unsigned int value;
+ size_t k;
+
+ octet = pointer[0];
+ width = (octet & 0x80) == 0x00 ? 1 :
+ (octet & 0xE0) == 0xC0 ? 2 :
+ (octet & 0xF0) == 0xE0 ? 3 :
+ (octet & 0xF8) == 0xF0 ? 4 : 0;
+ value = (octet & 0x80) == 0x00 ? octet & 0x7F :
+ (octet & 0xE0) == 0xC0 ? octet & 0x1F :
+ (octet & 0xF0) == 0xE0 ? octet & 0x0F :
+ (octet & 0xF8) == 0xF0 ? octet & 0x07 : 0;
+ if (!width) return 0;
+ if (pointer+width > end) return 0;
+ for (k = 1; k < width; k ++) {
+ octet = pointer[k];
+ if ((octet & 0xC0) != 0x80) return 0;
+ value = (value << 6) + (octet & 0x3F);
+ }
+ if (!((width == 1) ||
+ (width == 2 && value >= 0x80) ||
+ (width == 3 && value >= 0x800) ||
+ (width == 4 && value >= 0x10000))) return 0;
+
+ pointer += width;
+ }
+
+ return 1;
+}
+
+/*
+ * Create STREAM-START.
+ */
+
+YAML_DECLARE(int)
+yaml_stream_start_event_initialize(yaml_event_t *event,
+ yaml_encoding_t encoding)
+{
+ yaml_mark_t mark = { 0, 0, 0 };
+
+ assert(event); /* Non-NULL event object is expected. */
+
+ STREAM_START_EVENT_INIT(*event, encoding, mark, mark);
+
+ return 1;
+}
+
+/*
+ * Create STREAM-END.
+ */
+
+YAML_DECLARE(int)
+yaml_stream_end_event_initialize(yaml_event_t *event)
+{
+ yaml_mark_t mark = { 0, 0, 0 };
+
+ assert(event); /* Non-NULL event object is expected. */
+
+ STREAM_END_EVENT_INIT(*event, mark, mark);
+
+ return 1;
+}
+
+/*
+ * Create DOCUMENT-START.
+ */
+
+YAML_DECLARE(int)
+yaml_document_start_event_initialize(yaml_event_t *event,
+ yaml_version_directive_t *version_directive,
+ yaml_tag_directive_t *tag_directives_start,
+ yaml_tag_directive_t *tag_directives_end,
+ int implicit)
+{
+ struct {
+ yaml_error_type_t error;
+ } context;
+ yaml_mark_t mark = { 0, 0, 0 };
+ yaml_version_directive_t *version_directive_copy = NULL;
+ struct {
+ yaml_tag_directive_t *start;
+ yaml_tag_directive_t *end;
+ yaml_tag_directive_t *top;
+ } tag_directives_copy = { NULL, NULL, NULL };
+ yaml_tag_directive_t value = { NULL, NULL };
+
+ assert(event); /* Non-NULL event object is expected. */
+ assert((tag_directives_start && tag_directives_end) ||
+ (tag_directives_start == tag_directives_end));
+ /* Valid tag directives are expected. */
+
+ if (version_directive) {
+ version_directive_copy = yaml_malloc(sizeof(yaml_version_directive_t));
+ if (!version_directive_copy) goto error;
+ version_directive_copy->major = version_directive->major;
+ version_directive_copy->minor = version_directive->minor;
+ }
+
+ if (tag_directives_start != tag_directives_end) {
+ yaml_tag_directive_t *tag_directive;
+ if (!STACK_INIT(&context, tag_directives_copy, INITIAL_STACK_SIZE))
+ goto error;
+ for (tag_directive = tag_directives_start;
+ tag_directive != tag_directives_end; tag_directive ++) {
+ assert(tag_directive->handle);
+ assert(tag_directive->prefix);
+ if (!yaml_check_utf8(tag_directive->handle,
+ strlen((char *)tag_directive->handle)))
+ goto error;
+ if (!yaml_check_utf8(tag_directive->prefix,
+ strlen((char *)tag_directive->prefix)))
+ goto error;
+ value.handle = yaml_strdup(tag_directive->handle);
+ value.prefix = yaml_strdup(tag_directive->prefix);
+ if (!value.handle || !value.prefix) goto error;
+ if (!PUSH(&context, tag_directives_copy, value))
+ goto error;
+ value.handle = NULL;
+ value.prefix = NULL;
+ }
+ }
+
+ DOCUMENT_START_EVENT_INIT(*event, version_directive_copy,
+ tag_directives_copy.start, tag_directives_copy.top,
+ implicit, mark, mark);
+
+ return 1;
+
+error:
+ yaml_free(version_directive_copy);
+ while (!STACK_EMPTY(context, tag_directives_copy)) {
+ yaml_tag_directive_t value = POP(context, tag_directives_copy);
+ yaml_free(value.handle);
+ yaml_free(value.prefix);
+ }
+ STACK_DEL(context, tag_directives_copy);
+ yaml_free(value.handle);
+ yaml_free(value.prefix);
+
+ return 0;
+}
+
+/*
+ * Create DOCUMENT-END.
+ */
+
+YAML_DECLARE(int)
+yaml_document_end_event_initialize(yaml_event_t *event, int implicit)
+{
+ yaml_mark_t mark = { 0, 0, 0 };
+
+ assert(event); /* Non-NULL emitter object is expected. */
+
+ DOCUMENT_END_EVENT_INIT(*event, implicit, mark, mark);
+
+ return 1;
+}
+
+/*
+ * Create ALIAS.
+ */
+
+YAML_DECLARE(int)
+yaml_alias_event_initialize(yaml_event_t *event, yaml_char_t *anchor)
+{
+ yaml_mark_t mark = { 0, 0, 0 };
+ yaml_char_t *anchor_copy = NULL;
+
+ assert(event); /* Non-NULL event object is expected. */
+ assert(anchor); /* Non-NULL anchor is expected. */
+
+ if (!yaml_check_utf8(anchor, strlen((char *)anchor))) return 0;
+
+ anchor_copy = yaml_strdup(anchor);
+ if (!anchor_copy)
+ return 0;
+
+ ALIAS_EVENT_INIT(*event, anchor_copy, mark, mark);
+
+ return 1;
+}
+
+/*
+ * Create SCALAR.
+ */
+
+YAML_DECLARE(int)
+yaml_scalar_event_initialize(yaml_event_t *event,
+ yaml_char_t *anchor, yaml_char_t *tag,
+ yaml_char_t *value, int length,
+ int plain_implicit, int quoted_implicit,
+ yaml_scalar_style_t style)
+{
+ yaml_mark_t mark = { 0, 0, 0 };
+ yaml_char_t *anchor_copy = NULL;
+ yaml_char_t *tag_copy = NULL;
+ yaml_char_t *value_copy = NULL;
+ size_t value_length;
+
+ assert(event); /* Non-NULL event object is expected. */
+ assert(value); /* Non-NULL anchor is expected. */
+
+ if (anchor) {
+ if (!yaml_check_utf8(anchor, strlen((char *)anchor))) goto error;
+ anchor_copy = yaml_strdup(anchor);
+ if (!anchor_copy) goto error;
+ }
+
+ if (tag) {
+ if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error;
+ tag_copy = yaml_strdup(tag);
+ if (!tag_copy) goto error;
+ }
+
+ if (length < 0) {
+ value_length = strlen((char *)value);
+ }
+ else {
+ value_length = (size_t)length;
+ }
+
+ if (!yaml_check_utf8(value, value_length)) goto error;
+ value_copy = yaml_malloc(value_length+1);
+ if (!value_copy) goto error;
+ memcpy(value_copy, value, value_length);
+ value_copy[value_length] = '\0';
+
+ SCALAR_EVENT_INIT(*event, anchor_copy, tag_copy, value_copy, value_length,
+ plain_implicit, quoted_implicit, style, mark, mark);
+
+ return 1;
+
+error:
+ yaml_free(anchor_copy);
+ yaml_free(tag_copy);
+ yaml_free(value_copy);
+
+ return 0;
+}
+
+/*
+ * Create SEQUENCE-START.
+ */
+
+YAML_DECLARE(int)
+yaml_sequence_start_event_initialize(yaml_event_t *event,
+ yaml_char_t *anchor, yaml_char_t *tag, int implicit,
+ yaml_sequence_style_t style)
+{
+ yaml_mark_t mark = { 0, 0, 0 };
+ yaml_char_t *anchor_copy = NULL;
+ yaml_char_t *tag_copy = NULL;
+
+ assert(event); /* Non-NULL event object is expected. */
+
+ if (anchor) {
+ if (!yaml_check_utf8(anchor, strlen((char *)anchor))) goto error;
+ anchor_copy = yaml_strdup(anchor);
+ if (!anchor_copy) goto error;
+ }
+
+ if (tag) {
+ if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error;
+ tag_copy = yaml_strdup(tag);
+ if (!tag_copy) goto error;
+ }
+
+ SEQUENCE_START_EVENT_INIT(*event, anchor_copy, tag_copy,
+ implicit, style, mark, mark);
+
+ return 1;
+
+error:
+ yaml_free(anchor_copy);
+ yaml_free(tag_copy);
+
+ return 0;
+}
+
+/*
+ * Create SEQUENCE-END.
+ */
+
+YAML_DECLARE(int)
+yaml_sequence_end_event_initialize(yaml_event_t *event)
+{
+ yaml_mark_t mark = { 0, 0, 0 };
+
+ assert(event); /* Non-NULL event object is expected. */
+
+ SEQUENCE_END_EVENT_INIT(*event, mark, mark);
+
+ return 1;
+}
+
+/*
+ * Create MAPPING-START.
+ */
+
+YAML_DECLARE(int)
+yaml_mapping_start_event_initialize(yaml_event_t *event,
+ yaml_char_t *anchor, yaml_char_t *tag, int implicit,
+ yaml_mapping_style_t style)
+{
+ yaml_mark_t mark = { 0, 0, 0 };
+ yaml_char_t *anchor_copy = NULL;
+ yaml_char_t *tag_copy = NULL;
+
+ assert(event); /* Non-NULL event object is expected. */
+
+ if (anchor) {
+ if (!yaml_check_utf8(anchor, strlen((char *)anchor))) goto error;
+ anchor_copy = yaml_strdup(anchor);
+ if (!anchor_copy) goto error;
+ }
+
+ if (tag) {
+ if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error;
+ tag_copy = yaml_strdup(tag);
+ if (!tag_copy) goto error;
+ }
+
+ MAPPING_START_EVENT_INIT(*event, anchor_copy, tag_copy,
+ implicit, style, mark, mark);
+
+ return 1;
+
+error:
+ yaml_free(anchor_copy);
+ yaml_free(tag_copy);
+
+ return 0;
+}
+
+/*
+ * Create MAPPING-END.
+ */
+
+YAML_DECLARE(int)
+yaml_mapping_end_event_initialize(yaml_event_t *event)
+{
+ yaml_mark_t mark = { 0, 0, 0 };
+
+ assert(event); /* Non-NULL event object is expected. */
+
+ MAPPING_END_EVENT_INIT(*event, mark, mark);
+
+ return 1;
+}
+
+/*
+ * Destroy an event object.
+ */
+
+YAML_DECLARE(void)
+yaml_event_delete(yaml_event_t *event)
+{
+ yaml_tag_directive_t *tag_directive;
+
+ assert(event); /* Non-NULL event object expected. */
+
+ switch (event->type)
+ {
+ case YAML_DOCUMENT_START_EVENT:
+ yaml_free(event->data.document_start.version_directive);
+ for (tag_directive = event->data.document_start.tag_directives.start;
+ tag_directive != event->data.document_start.tag_directives.end;
+ tag_directive++) {
+ yaml_free(tag_directive->handle);
+ yaml_free(tag_directive->prefix);
+ }
+ yaml_free(event->data.document_start.tag_directives.start);
+ break;
+
+ case YAML_ALIAS_EVENT:
+ yaml_free(event->data.alias.anchor);
+ break;
+
+ case YAML_SCALAR_EVENT:
+ yaml_free(event->data.scalar.anchor);
+ yaml_free(event->data.scalar.tag);
+ yaml_free(event->data.scalar.value);
+ break;
+
+ case YAML_SEQUENCE_START_EVENT:
+ yaml_free(event->data.sequence_start.anchor);
+ yaml_free(event->data.sequence_start.tag);
+ break;
+
+ case YAML_MAPPING_START_EVENT:
+ yaml_free(event->data.mapping_start.anchor);
+ yaml_free(event->data.mapping_start.tag);
+ break;
+
+ default:
+ break;
+ }
+
+ memset(event, 0, sizeof(yaml_event_t));
+}
+
+/*
+ * Create a document object.
+ */
+
+YAML_DECLARE(int)
+yaml_document_initialize(yaml_document_t *document,
+ yaml_version_directive_t *version_directive,
+ yaml_tag_directive_t *tag_directives_start,
+ yaml_tag_directive_t *tag_directives_end,
+ int start_implicit, int end_implicit)
+{
+ struct {
+ yaml_error_type_t error;
+ } context;
+ struct {
+ yaml_node_t *start;
+ yaml_node_t *end;
+ yaml_node_t *top;
+ } nodes = { NULL, NULL, NULL };
+ yaml_version_directive_t *version_directive_copy = NULL;
+ struct {
+ yaml_tag_directive_t *start;
+ yaml_tag_directive_t *end;
+ yaml_tag_directive_t *top;
+ } tag_directives_copy = { NULL, NULL, NULL };
+ yaml_tag_directive_t value = { NULL, NULL };
+ yaml_mark_t mark = { 0, 0, 0 };
+
+ assert(document); /* Non-NULL document object is expected. */
+ assert((tag_directives_start && tag_directives_end) ||
+ (tag_directives_start == tag_directives_end));
+ /* Valid tag directives are expected. */
+
+ if (!STACK_INIT(&context, nodes, INITIAL_STACK_SIZE)) goto error;
+
+ if (version_directive) {
+ version_directive_copy = yaml_malloc(sizeof(yaml_version_directive_t));
+ if (!version_directive_copy) goto error;
+ version_directive_copy->major = version_directive->major;
+ version_directive_copy->minor = version_directive->minor;
+ }
+
+ if (tag_directives_start != tag_directives_end) {
+ yaml_tag_directive_t *tag_directive;
+ if (!STACK_INIT(&context, tag_directives_copy, INITIAL_STACK_SIZE))
+ goto error;
+ for (tag_directive = tag_directives_start;
+ tag_directive != tag_directives_end; tag_directive ++) {
+ assert(tag_directive->handle);
+ assert(tag_directive->prefix);
+ if (!yaml_check_utf8(tag_directive->handle,
+ strlen((char *)tag_directive->handle)))
+ goto error;
+ if (!yaml_check_utf8(tag_directive->prefix,
+ strlen((char *)tag_directive->prefix)))
+ goto error;
+ value.handle = yaml_strdup(tag_directive->handle);
+ value.prefix = yaml_strdup(tag_directive->prefix);
+ if (!value.handle || !value.prefix) goto error;
+ if (!PUSH(&context, tag_directives_copy, value))
+ goto error;
+ value.handle = NULL;
+ value.prefix = NULL;
+ }
+ }
+
+ DOCUMENT_INIT(*document, nodes.start, nodes.end, version_directive_copy,
+ tag_directives_copy.start, tag_directives_copy.top,
+ start_implicit, end_implicit, mark, mark);
+
+ return 1;
+
+error:
+ STACK_DEL(&context, nodes);
+ yaml_free(version_directive_copy);
+ while (!STACK_EMPTY(&context, tag_directives_copy)) {
+ yaml_tag_directive_t value = POP(&context, tag_directives_copy);
+ yaml_free(value.handle);
+ yaml_free(value.prefix);
+ }
+ STACK_DEL(&context, tag_directives_copy);
+ yaml_free(value.handle);
+ yaml_free(value.prefix);
+
+ return 0;
+}
+
+/*
+ * Destroy a document object.
+ */
+
+YAML_DECLARE(void)
+yaml_document_delete(yaml_document_t *document)
+{
+ struct {
+ yaml_error_type_t error;
+ } context;
+ yaml_tag_directive_t *tag_directive;
+
+ context.error = YAML_NO_ERROR; /* Eliminate a compliler warning. */
+
+ assert(document); /* Non-NULL document object is expected. */
+
+ while (!STACK_EMPTY(&context, document->nodes)) {
+ yaml_node_t node = POP(&context, document->nodes);
+ yaml_free(node.tag);
+ switch (node.type) {
+ case YAML_SCALAR_NODE:
+ yaml_free(node.data.scalar.value);
+ break;
+ case YAML_SEQUENCE_NODE:
+ STACK_DEL(&context, node.data.sequence.items);
+ break;
+ case YAML_MAPPING_NODE:
+ STACK_DEL(&context, node.data.mapping.pairs);
+ break;
+ default:
+ assert(0); /* Should not happen. */
+ }
+ }
+ STACK_DEL(&context, document->nodes);
+
+ yaml_free(document->version_directive);
+ for (tag_directive = document->tag_directives.start;
+ tag_directive != document->tag_directives.end;
+ tag_directive++) {
+ yaml_free(tag_directive->handle);
+ yaml_free(tag_directive->prefix);
+ }
+ yaml_free(document->tag_directives.start);
+
+ memset(document, 0, sizeof(yaml_document_t));
+}
+
+/**
+ * Get a document node.
+ */
+
+YAML_DECLARE(yaml_node_t *)
+yaml_document_get_node(yaml_document_t *document, int index)
+{
+ assert(document); /* Non-NULL document object is expected. */
+
+ if (index > 0 && document->nodes.start + index <= document->nodes.top) {
+ return document->nodes.start + index - 1;
+ }
+ return NULL;
+}
+
+/**
+ * Get the root object.
+ */
+
+YAML_DECLARE(yaml_node_t *)
+yaml_document_get_root_node(yaml_document_t *document)
+{
+ assert(document); /* Non-NULL document object is expected. */
+
+ if (document->nodes.top != document->nodes.start) {
+ return document->nodes.start;
+ }
+ return NULL;
+}
+
+/*
+ * Add a scalar node to a document.
+ */
+
+YAML_DECLARE(int)
+yaml_document_add_scalar(yaml_document_t *document,
+ yaml_char_t *tag, yaml_char_t *value, int length,
+ yaml_scalar_style_t style)
+{
+ struct {
+ yaml_error_type_t error;
+ } context;
+ yaml_mark_t mark = { 0, 0, 0 };
+ yaml_char_t *tag_copy = NULL;
+ yaml_char_t *value_copy = NULL;
+ yaml_node_t node;
+ size_t value_length;
+ ptrdiff_t ret;
+
+ assert(document); /* Non-NULL document object is expected. */
+ assert(value); /* Non-NULL value is expected. */
+
+ if (!tag) {
+ tag = (yaml_char_t *)YAML_DEFAULT_SCALAR_TAG;
+ }
+
+ if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error;
+ tag_copy = yaml_strdup(tag);
+ if (!tag_copy) goto error;
+
+ if (length < 0) {
+ value_length = strlen((char *)value);
+ }
+ else {
+ value_length = (size_t)length;
+ }
+
+ if (!yaml_check_utf8(value, value_length)) goto error;
+ value_copy = yaml_malloc(value_length+1);
+ if (!value_copy) goto error;
+ memcpy(value_copy, value, value_length);
+ value_copy[value_length] = '\0';
+
+ SCALAR_NODE_INIT(node, tag_copy, value_copy, value_length, style, mark, mark);
+ if (!PUSH(&context, document->nodes, node)) goto error;
+
+ ret = document->nodes.top - document->nodes.start;
+#if PTRDIFF_MAX > INT_MAX
+ if (ret > INT_MAX) goto error;
+#endif
+ return (int)ret;
+
+error:
+ yaml_free(tag_copy);
+ yaml_free(value_copy);
+
+ return 0;
+}
+
+/*
+ * Add a sequence node to a document.
+ */
+
+YAML_DECLARE(int)
+yaml_document_add_sequence(yaml_document_t *document,
+ yaml_char_t *tag, yaml_sequence_style_t style)
+{
+ struct {
+ yaml_error_type_t error;
+ } context;
+ yaml_mark_t mark = { 0, 0, 0 };
+ yaml_char_t *tag_copy = NULL;
+ struct {
+ yaml_node_item_t *start;
+ yaml_node_item_t *end;
+ yaml_node_item_t *top;
+ } items = { NULL, NULL, NULL };
+ yaml_node_t node;
+ ptrdiff_t ret;
+
+ assert(document); /* Non-NULL document object is expected. */
+
+ if (!tag) {
+ tag = (yaml_char_t *)YAML_DEFAULT_SEQUENCE_TAG;
+ }
+
+ if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error;
+ tag_copy = yaml_strdup(tag);
+ if (!tag_copy) goto error;
+
+ if (!STACK_INIT(&context, items, INITIAL_STACK_SIZE)) goto error;
+
+ SEQUENCE_NODE_INIT(node, tag_copy, items.start, items.end,
+ style, mark, mark);
+ if (!PUSH(&context, document->nodes, node)) goto error;
+
+ ret = document->nodes.top - document->nodes.start;
+#if PTRDIFF_MAX > INT_MAX
+ if (ret > INT_MAX) goto error;
+#endif
+ return (int)ret;
+
+error:
+ STACK_DEL(&context, items);
+ yaml_free(tag_copy);
+
+ return 0;
+}
+
+/*
+ * Add a mapping node to a document.
+ */
+
+YAML_DECLARE(int)
+yaml_document_add_mapping(yaml_document_t *document,
+ yaml_char_t *tag, yaml_mapping_style_t style)
+{
+ struct {
+ yaml_error_type_t error;
+ } context;
+ yaml_mark_t mark = { 0, 0, 0 };
+ yaml_char_t *tag_copy = NULL;
+ struct {
+ yaml_node_pair_t *start;
+ yaml_node_pair_t *end;
+ yaml_node_pair_t *top;
+ } pairs = { NULL, NULL, NULL };
+ yaml_node_t node;
+ ptrdiff_t ret;
+
+ assert(document); /* Non-NULL document object is expected. */
+
+ if (!tag) {
+ tag = (yaml_char_t *)YAML_DEFAULT_MAPPING_TAG;
+ }
+
+ if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error;
+ tag_copy = yaml_strdup(tag);
+ if (!tag_copy) goto error;
+
+ if (!STACK_INIT(&context, pairs, INITIAL_STACK_SIZE)) goto error;
+
+ MAPPING_NODE_INIT(node, tag_copy, pairs.start, pairs.end,
+ style, mark, mark);
+ if (!PUSH(&context, document->nodes, node)) goto error;
+
+ ret = document->nodes.top - document->nodes.start;
+#if PTRDIFF_MAX > INT_MAX
+ if (ret > INT_MAX) goto error;
+#endif
+ return (int)ret;
+
+error:
+ STACK_DEL(&context, pairs);
+ yaml_free(tag_copy);
+
+ return 0;
+}
+
+/*
+ * Append an item to a sequence node.
+ */
+
+YAML_DECLARE(int)
+yaml_document_append_sequence_item(yaml_document_t *document,
+ int sequence, int item)
+{
+ struct {
+ yaml_error_type_t error;
+ } context;
+
+ assert(document); /* Non-NULL document is required. */
+ assert(sequence > 0
+ && document->nodes.start + sequence <= document->nodes.top);
+ /* Valid sequence id is required. */
+ assert(document->nodes.start[sequence-1].type == YAML_SEQUENCE_NODE);
+ /* A sequence node is required. */
+ assert(item > 0 && document->nodes.start + item <= document->nodes.top);
+ /* Valid item id is required. */
+
+ if (!PUSH(&context,
+ document->nodes.start[sequence-1].data.sequence.items, item))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Append a pair of a key and a value to a mapping node.
+ */
+
+YAML_DECLARE(int)
+yaml_document_append_mapping_pair(yaml_document_t *document,
+ int mapping, int key, int value)
+{
+ struct {
+ yaml_error_type_t error;
+ } context;
+
+ yaml_node_pair_t pair;
+
+ assert(document); /* Non-NULL document is required. */
+ assert(mapping > 0
+ && document->nodes.start + mapping <= document->nodes.top);
+ /* Valid mapping id is required. */
+ assert(document->nodes.start[mapping-1].type == YAML_MAPPING_NODE);
+ /* A mapping node is required. */
+ assert(key > 0 && document->nodes.start + key <= document->nodes.top);
+ /* Valid key id is required. */
+ assert(value > 0 && document->nodes.start + value <= document->nodes.top);
+ /* Valid value id is required. */
+
+ pair.key = key;
+ pair.value = value;
+
+ if (!PUSH(&context,
+ document->nodes.start[mapping-1].data.mapping.pairs, pair))
+ return 0;
+
+ return 1;
+}
+
+
diff --git a/ext/psych/yaml/config.h b/ext/psych/yaml/config.h
new file mode 100644
index 0000000..f54c27d
--- /dev/null
+++ b/ext/psych/yaml/config.h
@@ -0,0 +1,10 @@
+#define PACKAGE_NAME "yaml"
+#define PACKAGE_TARNAME "yaml"
+#define PACKAGE_VERSION "0.1.6"
+#define PACKAGE_STRING "yaml 0.1.6"
+#define PACKAGE_BUGREPORT "http://pyyaml.org/newticket?component libyaml"
+#define PACKAGE_URL ""
+#define YAML_VERSION_MAJOR 0
+#define YAML_VERSION_MINOR 1
+#define YAML_VERSION_PATCH 6
+#define YAML_VERSION_STRING "0.1.6"
diff --git a/ext/psych/yaml/dumper.c b/ext/psych/yaml/dumper.c
new file mode 100644
index 0000000..203c6a7
--- /dev/null
+++ b/ext/psych/yaml/dumper.c
@@ -0,0 +1,394 @@
+
+#include "yaml_private.h"
+
+/*
+ * API functions.
+ */
+
+YAML_DECLARE(int)
+yaml_emitter_open(yaml_emitter_t *emitter);
+
+YAML_DECLARE(int)
+yaml_emitter_close(yaml_emitter_t *emitter);
+
+YAML_DECLARE(int)
+yaml_emitter_dump(yaml_emitter_t *emitter, yaml_document_t *document);
+
+/*
+ * Clean up functions.
+ */
+
+static void
+yaml_emitter_delete_document_and_anchors(yaml_emitter_t *emitter);
+
+/*
+ * Anchor functions.
+ */
+
+static void
+yaml_emitter_anchor_node(yaml_emitter_t *emitter, int index);
+
+static yaml_char_t *
+yaml_emitter_generate_anchor(yaml_emitter_t *emitter, int anchor_id);
+
+
+/*
+ * Serialize functions.
+ */
+
+static int
+yaml_emitter_dump_node(yaml_emitter_t *emitter, int index);
+
+static int
+yaml_emitter_dump_alias(yaml_emitter_t *emitter, yaml_char_t *anchor);
+
+static int
+yaml_emitter_dump_scalar(yaml_emitter_t *emitter, yaml_node_t *node,
+ yaml_char_t *anchor);
+
+static int
+yaml_emitter_dump_sequence(yaml_emitter_t *emitter, yaml_node_t *node,
+ yaml_char_t *anchor);
+
+static int
+yaml_emitter_dump_mapping(yaml_emitter_t *emitter, yaml_node_t *node,
+ yaml_char_t *anchor);
+
+/*
+ * Issue a STREAM-START event.
+ */
+
+YAML_DECLARE(int)
+yaml_emitter_open(yaml_emitter_t *emitter)
+{
+ yaml_event_t event;
+ yaml_mark_t mark = { 0, 0, 0 };
+
+ assert(emitter); /* Non-NULL emitter object is required. */
+ assert(!emitter->opened); /* Emitter should not be opened yet. */
+
+ STREAM_START_EVENT_INIT(event, YAML_ANY_ENCODING, mark, mark);
+
+ if (!yaml_emitter_emit(emitter, &event)) {
+ return 0;
+ }
+
+ emitter->opened = 1;
+
+ return 1;
+}
+
+/*
+ * Issue a STREAM-END event.
+ */
+
+YAML_DECLARE(int)
+yaml_emitter_close(yaml_emitter_t *emitter)
+{
+ yaml_event_t event;
+ yaml_mark_t mark = { 0, 0, 0 };
+
+ assert(emitter); /* Non-NULL emitter object is required. */
+ assert(emitter->opened); /* Emitter should be opened. */
+
+ if (emitter->closed) return 1;
+
+ STREAM_END_EVENT_INIT(event, mark, mark);
+
+ if (!yaml_emitter_emit(emitter, &event)) {
+ return 0;
+ }
+
+ emitter->closed = 1;
+
+ return 1;
+}
+
+/*
+ * Dump a YAML document.
+ */
+
+YAML_DECLARE(int)
+yaml_emitter_dump(yaml_emitter_t *emitter, yaml_document_t *document)
+{
+ yaml_event_t event;
+ yaml_mark_t mark = { 0, 0, 0 };
+
+ assert(emitter); /* Non-NULL emitter object is required. */
+ assert(document); /* Non-NULL emitter object is expected. */
+
+ emitter->document = document;
+
+ if (!emitter->opened) {
+ if (!yaml_emitter_open(emitter)) goto error;
+ }
+
+ if (STACK_EMPTY(emitter, document->nodes)) {
+ if (!yaml_emitter_close(emitter)) goto error;
+ yaml_emitter_delete_document_and_anchors(emitter);
+ return 1;
+ }
+
+ assert(emitter->opened); /* Emitter should be opened. */
+
+ emitter->anchors = yaml_malloc(sizeof(*(emitter->anchors))
+ * (document->nodes.top - document->nodes.start));
+ if (!emitter->anchors) goto error;
+ memset(emitter->anchors, 0, sizeof(*(emitter->anchors))
+ * (document->nodes.top - document->nodes.start));
+
+ DOCUMENT_START_EVENT_INIT(event, document->version_directive,
+ document->tag_directives.start, document->tag_directives.end,
+ document->start_implicit, mark, mark);
+ if (!yaml_emitter_emit(emitter, &event)) goto error;
+
+ yaml_emitter_anchor_node(emitter, 1);
+ if (!yaml_emitter_dump_node(emitter, 1)) goto error;
+
+ DOCUMENT_END_EVENT_INIT(event, document->end_implicit, mark, mark);
+ if (!yaml_emitter_emit(emitter, &event)) goto error;
+
+ yaml_emitter_delete_document_and_anchors(emitter);
+
+ return 1;
+
+error:
+
+ yaml_emitter_delete_document_and_anchors(emitter);
+
+ return 0;
+}
+
+/*
+ * Clean up the emitter object after a document is dumped.
+ */
+
+static void
+yaml_emitter_delete_document_and_anchors(yaml_emitter_t *emitter)
+{
+ int index;
+
+ if (!emitter->anchors) {
+ yaml_document_delete(emitter->document);
+ emitter->document = NULL;
+ return;
+ }
+
+ for (index = 0; emitter->document->nodes.start + index
+ < emitter->document->nodes.top; index ++) {
+ yaml_node_t node = emitter->document->nodes.start[index];
+ if (!emitter->anchors[index].serialized) {
+ yaml_free(node.tag);
+ if (node.type == YAML_SCALAR_NODE) {
+ yaml_free(node.data.scalar.value);
+ }
+ }
+ if (node.type == YAML_SEQUENCE_NODE) {
+ STACK_DEL(emitter, node.data.sequence.items);
+ }
+ if (node.type == YAML_MAPPING_NODE) {
+ STACK_DEL(emitter, node.data.mapping.pairs);
+ }
+ }
+
+ STACK_DEL(emitter, emitter->document->nodes);
+ yaml_free(emitter->anchors);
+
+ emitter->anchors = NULL;
+ emitter->last_anchor_id = 0;
+ emitter->document = NULL;
+}
+
+/*
+ * Check the references of a node and assign the anchor id if needed.
+ */
+
+static void
+yaml_emitter_anchor_node(yaml_emitter_t *emitter, int index)
+{
+ yaml_node_t *node = emitter->document->nodes.start + index - 1;
+ yaml_node_item_t *item;
+ yaml_node_pair_t *pair;
+
+ emitter->anchors[index-1].references ++;
+
+ if (emitter->anchors[index-1].references == 1) {
+ switch (node->type) {
+ case YAML_SEQUENCE_NODE:
+ for (item = node->data.sequence.items.start;
+ item < node->data.sequence.items.top; item ++) {
+ yaml_emitter_anchor_node(emitter, *item);
+ }
+ break;
+ case YAML_MAPPING_NODE:
+ for (pair = node->data.mapping.pairs.start;
+ pair < node->data.mapping.pairs.top; pair ++) {
+ yaml_emitter_anchor_node(emitter, pair->key);
+ yaml_emitter_anchor_node(emitter, pair->value);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ else if (emitter->anchors[index-1].references == 2) {
+ emitter->anchors[index-1].anchor = (++ emitter->last_anchor_id);
+ }
+}
+
+/*
+ * Generate a textual representation for an anchor.
+ */
+
+#define ANCHOR_TEMPLATE "id%03d"
+#define ANCHOR_TEMPLATE_LENGTH 16
+
+static yaml_char_t *
+yaml_emitter_generate_anchor(yaml_emitter_t *emitter, int anchor_id)
+{
+ yaml_char_t *anchor = yaml_malloc(ANCHOR_TEMPLATE_LENGTH);
+
+ if (!anchor) return NULL;
+
+ sprintf((char *)anchor, ANCHOR_TEMPLATE, anchor_id);
+
+ return anchor;
+}
+
+/*
+ * Serialize a node.
+ */
+
+static int
+yaml_emitter_dump_node(yaml_emitter_t *emitter, int index)
+{
+ yaml_node_t *node = emitter->document->nodes.start + index - 1;
+ int anchor_id = emitter->anchors[index-1].anchor;
+ yaml_char_t *anchor = NULL;
+
+ if (anchor_id) {
+ anchor = yaml_emitter_generate_anchor(emitter, anchor_id);
+ if (!anchor) return 0;
+ }
+
+ if (emitter->anchors[index-1].serialized) {
+ return yaml_emitter_dump_alias(emitter, anchor);
+ }
+
+ emitter->anchors[index-1].serialized = 1;
+
+ switch (node->type) {
+ case YAML_SCALAR_NODE:
+ return yaml_emitter_dump_scalar(emitter, node, anchor);
+ case YAML_SEQUENCE_NODE:
+ return yaml_emitter_dump_sequence(emitter, node, anchor);
+ case YAML_MAPPING_NODE:
+ return yaml_emitter_dump_mapping(emitter, node, anchor);
+ default:
+ assert(0); /* Could not happen. */
+ break;
+ }
+
+ return 0; /* Could not happen. */
+}
+
+/*
+ * Serialize an alias.
+ */
+
+static int
+yaml_emitter_dump_alias(yaml_emitter_t *emitter, yaml_char_t *anchor)
+{
+ yaml_event_t event;
+ yaml_mark_t mark = { 0, 0, 0 };
+
+ ALIAS_EVENT_INIT(event, anchor, mark, mark);
+
+ return yaml_emitter_emit(emitter, &event);
+}
+
+/*
+ * Serialize a scalar.
+ */
+
+static int
+yaml_emitter_dump_scalar(yaml_emitter_t *emitter, yaml_node_t *node,
+ yaml_char_t *anchor)
+{
+ yaml_event_t event;
+ yaml_mark_t mark = { 0, 0, 0 };
+
+ int plain_implicit = (strcmp((char *)node->tag,
+ YAML_DEFAULT_SCALAR_TAG) == 0);
+ int quoted_implicit = (strcmp((char *)node->tag,
+ YAML_DEFAULT_SCALAR_TAG) == 0);
+
+ SCALAR_EVENT_INIT(event, anchor, node->tag, node->data.scalar.value,
+ node->data.scalar.length, plain_implicit, quoted_implicit,
+ node->data.scalar.style, mark, mark);
+
+ return yaml_emitter_emit(emitter, &event);
+}
+
+/*
+ * Serialize a sequence.
+ */
+
+static int
+yaml_emitter_dump_sequence(yaml_emitter_t *emitter, yaml_node_t *node,
+ yaml_char_t *anchor)
+{
+ yaml_event_t event;
+ yaml_mark_t mark = { 0, 0, 0 };
+
+ int implicit = (strcmp((char *)node->tag, YAML_DEFAULT_SEQUENCE_TAG) == 0);
+
+ yaml_node_item_t *item;
+
+ SEQUENCE_START_EVENT_INIT(event, anchor, node->tag, implicit,
+ node->data.sequence.style, mark, mark);
+ if (!yaml_emitter_emit(emitter, &event)) return 0;
+
+ for (item = node->data.sequence.items.start;
+ item < node->data.sequence.items.top; item ++) {
+ if (!yaml_emitter_dump_node(emitter, *item)) return 0;
+ }
+
+ SEQUENCE_END_EVENT_INIT(event, mark, mark);
+ if (!yaml_emitter_emit(emitter, &event)) return 0;
+
+ return 1;
+}
+
+/*
+ * Serialize a mapping.
+ */
+
+static int
+yaml_emitter_dump_mapping(yaml_emitter_t *emitter, yaml_node_t *node,
+ yaml_char_t *anchor)
+{
+ yaml_event_t event;
+ yaml_mark_t mark = { 0, 0, 0 };
+
+ int implicit = (strcmp((char *)node->tag, YAML_DEFAULT_MAPPING_TAG) == 0);
+
+ yaml_node_pair_t *pair;
+
+ MAPPING_START_EVENT_INIT(event, anchor, node->tag, implicit,
+ node->data.mapping.style, mark, mark);
+ if (!yaml_emitter_emit(emitter, &event)) return 0;
+
+ for (pair = node->data.mapping.pairs.start;
+ pair < node->data.mapping.pairs.top; pair ++) {
+ if (!yaml_emitter_dump_node(emitter, pair->key)) return 0;
+ if (!yaml_emitter_dump_node(emitter, pair->value)) return 0;
+ }
+
+ MAPPING_END_EVENT_INIT(event, mark, mark);
+ if (!yaml_emitter_emit(emitter, &event)) return 0;
+
+ return 1;
+}
+
diff --git a/ext/psych/yaml/emitter.c b/ext/psych/yaml/emitter.c
new file mode 100644
index 0000000..bf84faf
--- /dev/null
+++ b/ext/psych/yaml/emitter.c
@@ -0,0 +1,2329 @@
+
+#include "yaml_private.h"
+
+/*
+ * Flush the buffer if needed.
+ */
+
+#define FLUSH(emitter) \
+ ((emitter->buffer.pointer+5 < emitter->buffer.end) \
+ || yaml_emitter_flush(emitter))
+
+/*
+ * Put a character to the output buffer.
+ */
+
+#define PUT(emitter,value) \
+ (FLUSH(emitter) \
+ && (*(emitter->buffer.pointer++) = (yaml_char_t)(value), \
+ emitter->column ++, \
+ 1))
+
+/*
+ * Put a line break to the output buffer.
+ */
+
+#define PUT_BREAK(emitter) \
+ (FLUSH(emitter) \
+ && ((emitter->line_break == YAML_CR_BREAK ? \
+ (*(emitter->buffer.pointer++) = (yaml_char_t) '\r') : \
+ emitter->line_break == YAML_LN_BREAK ? \
+ (*(emitter->buffer.pointer++) = (yaml_char_t) '\n') : \
+ emitter->line_break == YAML_CRLN_BREAK ? \
+ (*(emitter->buffer.pointer++) = (yaml_char_t) '\r', \
+ *(emitter->buffer.pointer++) = (yaml_char_t) '\n') : 0), \
+ emitter->column = 0, \
+ emitter->line ++, \
+ 1))
+
+/*
+ * Copy a character from a string into buffer.
+ */
+
+#define WRITE(emitter,string) \
+ (FLUSH(emitter) \
+ && (COPY(emitter->buffer,string), \
+ emitter->column ++, \
+ 1))
+
+/*
+ * Copy a line break character from a string into buffer.
+ */
+
+#define WRITE_BREAK(emitter,string) \
+ (FLUSH(emitter) \
+ && (CHECK(string,'\n') ? \
+ ((void)PUT_BREAK(emitter), \
+ string.pointer ++, \
+ 1) : \
+ (COPY(emitter->buffer,string), \
+ emitter->column = 0, \
+ emitter->line ++, \
+ 1)))
+
+/*
+ * API functions.
+ */
+
+YAML_DECLARE(int)
+yaml_emitter_emit(yaml_emitter_t *emitter, yaml_event_t *event);
+
+/*
+ * Utility functions.
+ */
+
+static int
+yaml_emitter_set_emitter_error(yaml_emitter_t *emitter, const char *problem);
+
+static int
+yaml_emitter_need_more_events(yaml_emitter_t *emitter);
+
+static int
+yaml_emitter_append_tag_directive(yaml_emitter_t *emitter,
+ yaml_tag_directive_t value, int allow_duplicates);
+
+static int
+yaml_emitter_increase_indent(yaml_emitter_t *emitter,
+ int flow, int indentless);
+
+/*
+ * State functions.
+ */
+
+static int
+yaml_emitter_state_machine(yaml_emitter_t *emitter, yaml_event_t *event);
+
+static int
+yaml_emitter_emit_stream_start(yaml_emitter_t *emitter,
+ yaml_event_t *event);
+
+static int
+yaml_emitter_emit_document_start(yaml_emitter_t *emitter,
+ yaml_event_t *event, int first);
+
+static int
+yaml_emitter_emit_document_content(yaml_emitter_t *emitter,
+ yaml_event_t *event);
+
+static int
+yaml_emitter_emit_document_end(yaml_emitter_t *emitter,
+ yaml_event_t *event);
+
+static int
+yaml_emitter_emit_flow_sequence_item(yaml_emitter_t *emitter,
+ yaml_event_t *event, int first);
+
+static int
+yaml_emitter_emit_flow_mapping_key(yaml_emitter_t *emitter,
+ yaml_event_t *event, int first);
+
+static int
+yaml_emitter_emit_flow_mapping_value(yaml_emitter_t *emitter,
+ yaml_event_t *event, int simple);
+
+static int
+yaml_emitter_emit_block_sequence_item(yaml_emitter_t *emitter,
+ yaml_event_t *event, int first);
+
+static int
+yaml_emitter_emit_block_mapping_key(yaml_emitter_t *emitter,
+ yaml_event_t *event, int first);
+
+static int
+yaml_emitter_emit_block_mapping_value(yaml_emitter_t *emitter,
+ yaml_event_t *event, int simple);
+
+static int
+yaml_emitter_emit_node(yaml_emitter_t *emitter, yaml_event_t *event,
+ int root, int sequence, int mapping, int simple_key);
+
+static int
+yaml_emitter_emit_alias(yaml_emitter_t *emitter, yaml_event_t *event);
+
+static int
+yaml_emitter_emit_scalar(yaml_emitter_t *emitter, yaml_event_t *event);
+
+static int
+yaml_emitter_emit_sequence_start(yaml_emitter_t *emitter, yaml_event_t *event);
+
+static int
+yaml_emitter_emit_mapping_start(yaml_emitter_t *emitter, yaml_event_t *event);
+
+/*
+ * Checkers.
+ */
+
+static int
+yaml_emitter_check_empty_document(yaml_emitter_t *emitter);
+
+static int
+yaml_emitter_check_empty_sequence(yaml_emitter_t *emitter);
+
+static int
+yaml_emitter_check_empty_mapping(yaml_emitter_t *emitter);
+
+static int
+yaml_emitter_check_simple_key(yaml_emitter_t *emitter);
+
+static int
+yaml_emitter_select_scalar_style(yaml_emitter_t *emitter, yaml_event_t *event);
+
+/*
+ * Processors.
+ */
+
+static int
+yaml_emitter_process_anchor(yaml_emitter_t *emitter);
+
+static int
+yaml_emitter_process_tag(yaml_emitter_t *emitter);
+
+static int
+yaml_emitter_process_scalar(yaml_emitter_t *emitter);
+
+/*
+ * Analyzers.
+ */
+
+static int
+yaml_emitter_analyze_version_directive(yaml_emitter_t *emitter,
+ yaml_version_directive_t version_directive);
+
+static int
+yaml_emitter_analyze_tag_directive(yaml_emitter_t *emitter,
+ yaml_tag_directive_t tag_directive);
+
+static int
+yaml_emitter_analyze_anchor(yaml_emitter_t *emitter,
+ yaml_char_t *anchor, int alias);
+
+static int
+yaml_emitter_analyze_tag(yaml_emitter_t *emitter,
+ yaml_char_t *tag);
+
+static int
+yaml_emitter_analyze_scalar(yaml_emitter_t *emitter,
+ yaml_char_t *value, size_t length);
+
+static int
+yaml_emitter_analyze_event(yaml_emitter_t *emitter,
+ yaml_event_t *event);
+
+/*
+ * Writers.
+ */
+
+static int
+yaml_emitter_write_bom(yaml_emitter_t *emitter);
+
+static int
+yaml_emitter_write_indent(yaml_emitter_t *emitter);
+
+static int
+yaml_emitter_write_indicator(yaml_emitter_t *emitter,
+ const char *indicator, int need_whitespace,
+ int is_whitespace, int is_indention);
+
+static int
+yaml_emitter_write_anchor(yaml_emitter_t *emitter,
+ yaml_char_t *value, size_t length);
+
+static int
+yaml_emitter_write_tag_handle(yaml_emitter_t *emitter,
+ yaml_char_t *value, size_t length);
+
+static int
+yaml_emitter_write_tag_content(yaml_emitter_t *emitter,
+ yaml_char_t *value, size_t length, int need_whitespace);
+
+static int
+yaml_emitter_write_plain_scalar(yaml_emitter_t *emitter,
+ yaml_char_t *value, size_t length, int allow_breaks);
+
+static int
+yaml_emitter_write_single_quoted_scalar(yaml_emitter_t *emitter,
+ yaml_char_t *value, size_t length, int allow_breaks);
+
+static int
+yaml_emitter_write_double_quoted_scalar(yaml_emitter_t *emitter,
+ yaml_char_t *value, size_t length, int allow_breaks);
+
+static int
+yaml_emitter_write_block_scalar_hints(yaml_emitter_t *emitter,
+ yaml_string_t string);
+
+static int
+yaml_emitter_write_literal_scalar(yaml_emitter_t *emitter,
+ yaml_char_t *value, size_t length);
+
+static int
+yaml_emitter_write_folded_scalar(yaml_emitter_t *emitter,
+ yaml_char_t *value, size_t length);
+
+/*
+ * Set an emitter error and return 0.
+ */
+
+static int
+yaml_emitter_set_emitter_error(yaml_emitter_t *emitter, const char *problem)
+{
+ emitter->error = YAML_EMITTER_ERROR;
+ emitter->problem = problem;
+
+ return 0;
+}
+
+/*
+ * Emit an event.
+ */
+
+YAML_DECLARE(int)
+yaml_emitter_emit(yaml_emitter_t *emitter, yaml_event_t *event)
+{
+ if (!ENQUEUE(emitter, emitter->events, *event)) {
+ yaml_event_delete(event);
+ return 0;
+ }
+
+ while (!yaml_emitter_need_more_events(emitter)) {
+ if (!yaml_emitter_analyze_event(emitter, emitter->events.head))
+ return 0;
+ if (!yaml_emitter_state_machine(emitter, emitter->events.head))
+ return 0;
+ yaml_event_delete(&DEQUEUE(emitter, emitter->events));
+ }
+
+ return 1;
+}
+
+/*
+ * Check if we need to accumulate more events before emitting.
+ *
+ * We accumulate extra
+ * - 1 event for DOCUMENT-START
+ * - 2 events for SEQUENCE-START
+ * - 3 events for MAPPING-START
+ */
+
+static int
+yaml_emitter_need_more_events(yaml_emitter_t *emitter)
+{
+ int level = 0;
+ int accumulate = 0;
+ yaml_event_t *event;
+
+ if (QUEUE_EMPTY(emitter, emitter->events))
+ return 1;
+
+ switch (emitter->events.head->type) {
+ case YAML_DOCUMENT_START_EVENT:
+ accumulate = 1;
+ break;
+ case YAML_SEQUENCE_START_EVENT:
+ accumulate = 2;
+ break;
+ case YAML_MAPPING_START_EVENT:
+ accumulate = 3;
+ break;
+ default:
+ return 0;
+ }
+
+ if (emitter->events.tail - emitter->events.head > accumulate)
+ return 0;
+
+ for (event = emitter->events.head; event != emitter->events.tail; event ++) {
+ switch (event->type) {
+ case YAML_STREAM_START_EVENT:
+ case YAML_DOCUMENT_START_EVENT:
+ case YAML_SEQUENCE_START_EVENT:
+ case YAML_MAPPING_START_EVENT:
+ level += 1;
+ break;
+ case YAML_STREAM_END_EVENT:
+ case YAML_DOCUMENT_END_EVENT:
+ case YAML_SEQUENCE_END_EVENT:
+ case YAML_MAPPING_END_EVENT:
+ level -= 1;
+ break;
+ default:
+ break;
+ }
+ if (!level)
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Append a directive to the directives stack.
+ */
+
+static int
+yaml_emitter_append_tag_directive(yaml_emitter_t *emitter,
+ yaml_tag_directive_t value, int allow_duplicates)
+{
+ yaml_tag_directive_t *tag_directive;
+ yaml_tag_directive_t copy = { NULL, NULL };
+
+ for (tag_directive = emitter->tag_directives.start;
+ tag_directive != emitter->tag_directives.top; tag_directive ++) {
+ if (strcmp((char *)value.handle, (char *)tag_directive->handle) == 0) {
+ if (allow_duplicates)
+ return 1;
+ return yaml_emitter_set_emitter_error(emitter,
+ "duplicate %TAG directive");
+ }
+ }
+
+ copy.handle = yaml_strdup(value.handle);
+ copy.prefix = yaml_strdup(value.prefix);
+ if (!copy.handle || !copy.prefix) {
+ emitter->error = YAML_MEMORY_ERROR;
+ goto error;
+ }
+
+ if (!PUSH(emitter, emitter->tag_directives, copy))
+ goto error;
+
+ return 1;
+
+error:
+ yaml_free(copy.handle);
+ yaml_free(copy.prefix);
+ return 0;
+}
+
+/*
+ * Increase the indentation level.
+ */
+
+static int
+yaml_emitter_increase_indent(yaml_emitter_t *emitter,
+ int flow, int indentless)
+{
+ if (!PUSH(emitter, emitter->indents, emitter->indent))
+ return 0;
+
+ if (emitter->indent < 0) {
+ emitter->indent = flow ? emitter->best_indent : 0;
+ }
+ else if (!indentless) {
+ emitter->indent += emitter->best_indent;
+ }
+
+ return 1;
+}
+
+/*
+ * State dispatcher.
+ */
+
+static int
+yaml_emitter_state_machine(yaml_emitter_t *emitter, yaml_event_t *event)
+{
+ switch (emitter->state)
+ {
+ case YAML_EMIT_STREAM_START_STATE:
+ return yaml_emitter_emit_stream_start(emitter, event);
+
+ case YAML_EMIT_FIRST_DOCUMENT_START_STATE:
+ return yaml_emitter_emit_document_start(emitter, event, 1);
+
+ case YAML_EMIT_DOCUMENT_START_STATE:
+ return yaml_emitter_emit_document_start(emitter, event, 0);
+
+ case YAML_EMIT_DOCUMENT_CONTENT_STATE:
+ return yaml_emitter_emit_document_content(emitter, event);
+
+ case YAML_EMIT_DOCUMENT_END_STATE:
+ return yaml_emitter_emit_document_end(emitter, event);
+
+ case YAML_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE:
+ return yaml_emitter_emit_flow_sequence_item(emitter, event, 1);
+
+ case YAML_EMIT_FLOW_SEQUENCE_ITEM_STATE:
+ return yaml_emitter_emit_flow_sequence_item(emitter, event, 0);
+
+ case YAML_EMIT_FLOW_MAPPING_FIRST_KEY_STATE:
+ return yaml_emitter_emit_flow_mapping_key(emitter, event, 1);
+
+ case YAML_EMIT_FLOW_MAPPING_KEY_STATE:
+ return yaml_emitter_emit_flow_mapping_key(emitter, event, 0);
+
+ case YAML_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE:
+ return yaml_emitter_emit_flow_mapping_value(emitter, event, 1);
+
+ case YAML_EMIT_FLOW_MAPPING_VALUE_STATE:
+ return yaml_emitter_emit_flow_mapping_value(emitter, event, 0);
+
+ case YAML_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE:
+ return yaml_emitter_emit_block_sequence_item(emitter, event, 1);
+
+ case YAML_EMIT_BLOCK_SEQUENCE_ITEM_STATE:
+ return yaml_emitter_emit_block_sequence_item(emitter, event, 0);
+
+ case YAML_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE:
+ return yaml_emitter_emit_block_mapping_key(emitter, event, 1);
+
+ case YAML_EMIT_BLOCK_MAPPING_KEY_STATE:
+ return yaml_emitter_emit_block_mapping_key(emitter, event, 0);
+
+ case YAML_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE:
+ return yaml_emitter_emit_block_mapping_value(emitter, event, 1);
+
+ case YAML_EMIT_BLOCK_MAPPING_VALUE_STATE:
+ return yaml_emitter_emit_block_mapping_value(emitter, event, 0);
+
+ case YAML_EMIT_END_STATE:
+ return yaml_emitter_set_emitter_error(emitter,
+ "expected nothing after STREAM-END");
+
+ default:
+ assert(1); /* Invalid state. */
+ }
+
+ return 0;
+}
+
+/*
+ * Expect STREAM-START.
+ */
+
+static int
+yaml_emitter_emit_stream_start(yaml_emitter_t *emitter,
+ yaml_event_t *event)
+{
+ if (event->type == YAML_STREAM_START_EVENT)
+ {
+ if (!emitter->encoding) {
+ emitter->encoding = event->data.stream_start.encoding;
+ }
+
+ if (!emitter->encoding) {
+ emitter->encoding = YAML_UTF8_ENCODING;
+ }
+
+ if (emitter->best_indent < 2 || emitter->best_indent > 9) {
+ emitter->best_indent = 2;
+ }
+
+ if (emitter->best_width >= 0
+ && emitter->best_width <= emitter->best_indent*2) {
+ emitter->best_width = 80;
+ }
+
+ if (emitter->best_width < 0) {
+ emitter->best_width = INT_MAX;
+ }
+
+ if (!emitter->line_break) {
+ emitter->line_break = YAML_LN_BREAK;
+ }
+
+ emitter->indent = -1;
+
+ emitter->line = 0;
+ emitter->column = 0;
+ emitter->whitespace = 1;
+ emitter->indention = 1;
+
+ if (emitter->encoding != YAML_UTF8_ENCODING) {
+ if (!yaml_emitter_write_bom(emitter))
+ return 0;
+ }
+
+ emitter->state = YAML_EMIT_FIRST_DOCUMENT_START_STATE;
+
+ return 1;
+ }
+
+ return yaml_emitter_set_emitter_error(emitter,
+ "expected STREAM-START");
+}
+
+/*
+ * Expect DOCUMENT-START or STREAM-END.
+ */
+
+static int
+yaml_emitter_emit_document_start(yaml_emitter_t *emitter,
+ yaml_event_t *event, int first)
+{
+ if (event->type == YAML_DOCUMENT_START_EVENT)
+ {
+ yaml_tag_directive_t default_tag_directives[] = {
+ {(yaml_char_t *)"!", (yaml_char_t *)"!"},
+ {(yaml_char_t *)"!!", (yaml_char_t *)"tag:yaml.org,2002:"},
+ {NULL, NULL}
+ };
+ yaml_tag_directive_t *tag_directive;
+ int implicit;
+
+ if (event->data.document_start.version_directive) {
+ if (!yaml_emitter_analyze_version_directive(emitter,
+ *event->data.document_start.version_directive))
+ return 0;
+ }
+
+ for (tag_directive = event->data.document_start.tag_directives.start;
+ tag_directive != event->data.document_start.tag_directives.end;
+ tag_directive ++) {
+ if (!yaml_emitter_analyze_tag_directive(emitter, *tag_directive))
+ return 0;
+ if (!yaml_emitter_append_tag_directive(emitter, *tag_directive, 0))
+ return 0;
+ }
+
+ for (tag_directive = default_tag_directives;
+ tag_directive->handle; tag_directive ++) {
+ if (!yaml_emitter_append_tag_directive(emitter, *tag_directive, 1))
+ return 0;
+ }
+
+ implicit = event->data.document_start.implicit;
+ if (!first || emitter->canonical) {
+ implicit = 0;
+ }
+
+ if ((event->data.document_start.version_directive ||
+ (event->data.document_start.tag_directives.start
+ != event->data.document_start.tag_directives.end)) &&
+ emitter->open_ended)
+ {
+ if (!yaml_emitter_write_indicator(emitter, "...", 1, 0, 0))
+ return 0;
+ if (!yaml_emitter_write_indent(emitter))
+ return 0;
+ }
+
+ if (event->data.document_start.version_directive) {
+ implicit = 0;
+ if (!yaml_emitter_write_indicator(emitter, "%YAML", 1, 0, 0))
+ return 0;
+ if (!yaml_emitter_write_indicator(emitter, "1.1", 1, 0, 0))
+ return 0;
+ if (!yaml_emitter_write_indent(emitter))
+ return 0;
+ }
+
+ if (event->data.document_start.tag_directives.start
+ != event->data.document_start.tag_directives.end) {
+ implicit = 0;
+ for (tag_directive = event->data.document_start.tag_directives.start;
+ tag_directive != event->data.document_start.tag_directives.end;
+ tag_directive ++) {
+ if (!yaml_emitter_write_indicator(emitter, "%TAG", 1, 0, 0))
+ return 0;
+ if (!yaml_emitter_write_tag_handle(emitter, tag_directive->handle,
+ strlen((char *)tag_directive->handle)))
+ return 0;
+ if (!yaml_emitter_write_tag_content(emitter, tag_directive->prefix,
+ strlen((char *)tag_directive->prefix), 1))
+ return 0;
+ if (!yaml_emitter_write_indent(emitter))
+ return 0;
+ }
+ }
+
+ if (yaml_emitter_check_empty_document(emitter)) {
+ implicit = 0;
+ }
+
+ if (!implicit) {
+ if (!yaml_emitter_write_indent(emitter))
+ return 0;
+ if (!yaml_emitter_write_indicator(emitter, "---", 1, 0, 0))
+ return 0;
+ if (emitter->canonical) {
+ if (!yaml_emitter_write_indent(emitter))
+ return 0;
+ }
+ }
+
+ emitter->state = YAML_EMIT_DOCUMENT_CONTENT_STATE;
+
+ return 1;
+ }
+
+ else if (event->type == YAML_STREAM_END_EVENT)
+ {
+ if (emitter->open_ended)
+ {
+ if (!yaml_emitter_write_indicator(emitter, "...", 1, 0, 0))
+ return 0;
+ if (!yaml_emitter_write_indent(emitter))
+ return 0;
+ }
+
+ if (!yaml_emitter_flush(emitter))
+ return 0;
+
+ emitter->state = YAML_EMIT_END_STATE;
+
+ return 1;
+ }
+
+ return yaml_emitter_set_emitter_error(emitter,
+ "expected DOCUMENT-START or STREAM-END");
+}
+
+/*
+ * Expect the root node.
+ */
+
+static int
+yaml_emitter_emit_document_content(yaml_emitter_t *emitter,
+ yaml_event_t *event)
+{
+ if (!PUSH(emitter, emitter->states, YAML_EMIT_DOCUMENT_END_STATE))
+ return 0;
+
+ return yaml_emitter_emit_node(emitter, event, 1, 0, 0, 0);
+}
+
+/*
+ * Expect DOCUMENT-END.
+ */
+
+static int
+yaml_emitter_emit_document_end(yaml_emitter_t *emitter,
+ yaml_event_t *event)
+{
+ if (event->type == YAML_DOCUMENT_END_EVENT)
+ {
+ if (!yaml_emitter_write_indent(emitter))
+ return 0;
+ if (!event->data.document_end.implicit) {
+ if (!yaml_emitter_write_indicator(emitter, "...", 1, 0, 0))
+ return 0;
+ if (!yaml_emitter_write_indent(emitter))
+ return 0;
+ }
+ if (!yaml_emitter_flush(emitter))
+ return 0;
+
+ emitter->state = YAML_EMIT_DOCUMENT_START_STATE;
+
+ while (!STACK_EMPTY(emitter, emitter->tag_directives)) {
+ yaml_tag_directive_t tag_directive = POP(emitter,
+ emitter->tag_directives);
+ yaml_free(tag_directive.handle);
+ yaml_free(tag_directive.prefix);
+ }
+
+ return 1;
+ }
+
+ return yaml_emitter_set_emitter_error(emitter,
+ "expected DOCUMENT-END");
+}
+
+/*
+ *
+ * Expect a flow item node.
+ */
+
+static int
+yaml_emitter_emit_flow_sequence_item(yaml_emitter_t *emitter,
+ yaml_event_t *event, int first)
+{
+ if (first)
+ {
+ if (!yaml_emitter_write_indicator(emitter, "[", 1, 1, 0))
+ return 0;
+ if (!yaml_emitter_increase_indent(emitter, 1, 0))
+ return 0;
+ emitter->flow_level ++;
+ }
+
+ if (event->type == YAML_SEQUENCE_END_EVENT)
+ {
+ emitter->flow_level --;
+ emitter->indent = POP(emitter, emitter->indents);
+ if (emitter->canonical && !first) {
+ if (!yaml_emitter_write_indicator(emitter, ",", 0, 0, 0))
+ return 0;
+ if (!yaml_emitter_write_indent(emitter))
+ return 0;
+ }
+ if (!yaml_emitter_write_indicator(emitter, "]", 0, 0, 0))
+ return 0;
+ emitter->state = POP(emitter, emitter->states);
+
+ return 1;
+ }
+
+ if (!first) {
+ if (!yaml_emitter_write_indicator(emitter, ",", 0, 0, 0))
+ return 0;
+ }
+
+ if (emitter->canonical || emitter->column > emitter->best_width) {
+ if (!yaml_emitter_write_indent(emitter))
+ return 0;
+ }
+ if (!PUSH(emitter, emitter->states, YAML_EMIT_FLOW_SEQUENCE_ITEM_STATE))
+ return 0;
+
+ return yaml_emitter_emit_node(emitter, event, 0, 1, 0, 0);
+}
+
+/*
+ * Expect a flow key node.
+ */
+
+static int
+yaml_emitter_emit_flow_mapping_key(yaml_emitter_t *emitter,
+ yaml_event_t *event, int first)
+{
+ if (first)
+ {
+ if (!yaml_emitter_write_indicator(emitter, "{", 1, 1, 0))
+ return 0;
+ if (!yaml_emitter_increase_indent(emitter, 1, 0))
+ return 0;
+ emitter->flow_level ++;
+ }
+
+ if (event->type == YAML_MAPPING_END_EVENT)
+ {
+ emitter->flow_level --;
+ emitter->indent = POP(emitter, emitter->indents);
+ if (emitter->canonical && !first) {
+ if (!yaml_emitter_write_indicator(emitter, ",", 0, 0, 0))
+ return 0;
+ if (!yaml_emitter_write_indent(emitter))
+ return 0;
+ }
+ if (!yaml_emitter_write_indicator(emitter, "}", 0, 0, 0))
+ return 0;
+ emitter->state = POP(emitter, emitter->states);
+
+ return 1;
+ }
+
+ if (!first) {
+ if (!yaml_emitter_write_indicator(emitter, ",", 0, 0, 0))
+ return 0;
+ }
+ if (emitter->canonical || emitter->column > emitter->best_width) {
+ if (!yaml_emitter_write_indent(emitter))
+ return 0;
+ }
+
+ if (!emitter->canonical && yaml_emitter_check_simple_key(emitter))
+ {
+ if (!PUSH(emitter, emitter->states,
+ YAML_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE))
+ return 0;
+
+ return yaml_emitter_emit_node(emitter, event, 0, 0, 1, 1);
+ }
+ else
+ {
+ if (!yaml_emitter_write_indicator(emitter, "?", 1, 0, 0))
+ return 0;
+ if (!PUSH(emitter, emitter->states,
+ YAML_EMIT_FLOW_MAPPING_VALUE_STATE))
+ return 0;
+
+ return yaml_emitter_emit_node(emitter, event, 0, 0, 1, 0);
+ }
+}
+
+/*
+ * Expect a flow value node.
+ */
+
+static int
+yaml_emitter_emit_flow_mapping_value(yaml_emitter_t *emitter,
+ yaml_event_t *event, int simple)
+{
+ if (simple) {
+ if (!yaml_emitter_write_indicator(emitter, ":", 0, 0, 0))
+ return 0;
+ }
+ else {
+ if (emitter->canonical || emitter->column > emitter->best_width) {
+ if (!yaml_emitter_write_indent(emitter))
+ return 0;
+ }
+ if (!yaml_emitter_write_indicator(emitter, ":", 1, 0, 0))
+ return 0;
+ }
+ if (!PUSH(emitter, emitter->states, YAML_EMIT_FLOW_MAPPING_KEY_STATE))
+ return 0;
+ return yaml_emitter_emit_node(emitter, event, 0, 0, 1, 0);
+}
+
+/*
+ * Expect a block item node.
+ */
+
+static int
+yaml_emitter_emit_block_sequence_item(yaml_emitter_t *emitter,
+ yaml_event_t *event, int first)
+{
+ if (first)
+ {
+ if (!yaml_emitter_increase_indent(emitter, 0,
+ (emitter->mapping_context && !emitter->indention)))
+ return 0;
+ }
+
+ if (event->type == YAML_SEQUENCE_END_EVENT)
+ {
+ emitter->indent = POP(emitter, emitter->indents);
+ emitter->state = POP(emitter, emitter->states);
+
+ return 1;
+ }
+
+ if (!yaml_emitter_write_indent(emitter))
+ return 0;
+ if (!yaml_emitter_write_indicator(emitter, "-", 1, 0, 1))
+ return 0;
+ if (!PUSH(emitter, emitter->states,
+ YAML_EMIT_BLOCK_SEQUENCE_ITEM_STATE))
+ return 0;
+
+ return yaml_emitter_emit_node(emitter, event, 0, 1, 0, 0);
+}
+
+/*
+ * Expect a block key node.
+ */
+
+static int
+yaml_emitter_emit_block_mapping_key(yaml_emitter_t *emitter,
+ yaml_event_t *event, int first)
+{
+ if (first)
+ {
+ if (!yaml_emitter_increase_indent(emitter, 0, 0))
+ return 0;
+ }
+
+ if (event->type == YAML_MAPPING_END_EVENT)
+ {
+ emitter->indent = POP(emitter, emitter->indents);
+ emitter->state = POP(emitter, emitter->states);
+
+ return 1;
+ }
+
+ if (!yaml_emitter_write_indent(emitter))
+ return 0;
+
+ if (yaml_emitter_check_simple_key(emitter))
+ {
+ if (!PUSH(emitter, emitter->states,
+ YAML_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE))
+ return 0;
+
+ return yaml_emitter_emit_node(emitter, event, 0, 0, 1, 1);
+ }
+ else
+ {
+ if (!yaml_emitter_write_indicator(emitter, "?", 1, 0, 1))
+ return 0;
+ if (!PUSH(emitter, emitter->states,
+ YAML_EMIT_BLOCK_MAPPING_VALUE_STATE))
+ return 0;
+
+ return yaml_emitter_emit_node(emitter, event, 0, 0, 1, 0);
+ }
+}
+
+/*
+ * Expect a block value node.
+ */
+
+static int
+yaml_emitter_emit_block_mapping_value(yaml_emitter_t *emitter,
+ yaml_event_t *event, int simple)
+{
+ if (simple) {
+ if (!yaml_emitter_write_indicator(emitter, ":", 0, 0, 0))
+ return 0;
+ }
+ else {
+ if (!yaml_emitter_write_indent(emitter))
+ return 0;
+ if (!yaml_emitter_write_indicator(emitter, ":", 1, 0, 1))
+ return 0;
+ }
+ if (!PUSH(emitter, emitter->states,
+ YAML_EMIT_BLOCK_MAPPING_KEY_STATE))
+ return 0;
+
+ return yaml_emitter_emit_node(emitter, event, 0, 0, 1, 0);
+}
+
+/*
+ * Expect a node.
+ */
+
+static int
+yaml_emitter_emit_node(yaml_emitter_t *emitter, yaml_event_t *event,
+ int root, int sequence, int mapping, int simple_key)
+{
+ emitter->root_context = root;
+ emitter->sequence_context = sequence;
+ emitter->mapping_context = mapping;
+ emitter->simple_key_context = simple_key;
+
+ switch (event->type)
+ {
+ case YAML_ALIAS_EVENT:
+ return yaml_emitter_emit_alias(emitter, event);
+
+ case YAML_SCALAR_EVENT:
+ return yaml_emitter_emit_scalar(emitter, event);
+
+ case YAML_SEQUENCE_START_EVENT:
+ return yaml_emitter_emit_sequence_start(emitter, event);
+
+ case YAML_MAPPING_START_EVENT:
+ return yaml_emitter_emit_mapping_start(emitter, event);
+
+ default:
+ return yaml_emitter_set_emitter_error(emitter,
+ "expected SCALAR, SEQUENCE-START, MAPPING-START, or ALIAS");
+ }
+
+ return 0;
+}
+
+/*
+ * Expect ALIAS.
+ */
+
+static int
+yaml_emitter_emit_alias(yaml_emitter_t *emitter, yaml_event_t *event)
+{
+ if (!yaml_emitter_process_anchor(emitter))
+ return 0;
+ emitter->state = POP(emitter, emitter->states);
+
+ return 1;
+}
+
+/*
+ * Expect SCALAR.
+ */
+
+static int
+yaml_emitter_emit_scalar(yaml_emitter_t *emitter, yaml_event_t *event)
+{
+ if (!yaml_emitter_select_scalar_style(emitter, event))
+ return 0;
+ if (!yaml_emitter_process_anchor(emitter))
+ return 0;
+ if (!yaml_emitter_process_tag(emitter))
+ return 0;
+ if (!yaml_emitter_increase_indent(emitter, 1, 0))
+ return 0;
+ if (!yaml_emitter_process_scalar(emitter))
+ return 0;
+ emitter->indent = POP(emitter, emitter->indents);
+ emitter->state = POP(emitter, emitter->states);
+
+ return 1;
+}
+
+/*
+ * Expect SEQUENCE-START.
+ */
+
+static int
+yaml_emitter_emit_sequence_start(yaml_emitter_t *emitter, yaml_event_t *event)
+{
+ if (!yaml_emitter_process_anchor(emitter))
+ return 0;
+ if (!yaml_emitter_process_tag(emitter))
+ return 0;
+
+ if (emitter->flow_level || emitter->canonical
+ || event->data.sequence_start.style == YAML_FLOW_SEQUENCE_STYLE
+ || yaml_emitter_check_empty_sequence(emitter)) {
+ emitter->state = YAML_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE;
+ }
+ else {
+ emitter->state = YAML_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE;
+ }
+
+ return 1;
+}
+
+/*
+ * Expect MAPPING-START.
+ */
+
+static int
+yaml_emitter_emit_mapping_start(yaml_emitter_t *emitter, yaml_event_t *event)
+{
+ if (!yaml_emitter_process_anchor(emitter))
+ return 0;
+ if (!yaml_emitter_process_tag(emitter))
+ return 0;
+
+ if (emitter->flow_level || emitter->canonical
+ || event->data.mapping_start.style == YAML_FLOW_MAPPING_STYLE
+ || yaml_emitter_check_empty_mapping(emitter)) {
+ emitter->state = YAML_EMIT_FLOW_MAPPING_FIRST_KEY_STATE;
+ }
+ else {
+ emitter->state = YAML_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE;
+ }
+
+ return 1;
+}
+
+/*
+ * Check if the document content is an empty scalar.
+ */
+
+static int
+yaml_emitter_check_empty_document(yaml_emitter_t *emitter)
+{
+ return 0;
+}
+
+/*
+ * Check if the next events represent an empty sequence.
+ */
+
+static int
+yaml_emitter_check_empty_sequence(yaml_emitter_t *emitter)
+{
+ if (emitter->events.tail - emitter->events.head < 2)
+ return 0;
+
+ return (emitter->events.head[0].type == YAML_SEQUENCE_START_EVENT
+ && emitter->events.head[1].type == YAML_SEQUENCE_END_EVENT);
+}
+
+/*
+ * Check if the next events represent an empty mapping.
+ */
+
+static int
+yaml_emitter_check_empty_mapping(yaml_emitter_t *emitter)
+{
+ if (emitter->events.tail - emitter->events.head < 2)
+ return 0;
+
+ return (emitter->events.head[0].type == YAML_MAPPING_START_EVENT
+ && emitter->events.head[1].type == YAML_MAPPING_END_EVENT);
+}
+
+/*
+ * Check if the next node can be expressed as a simple key.
+ */
+
+static int
+yaml_emitter_check_simple_key(yaml_emitter_t *emitter)
+{
+ yaml_event_t *event = emitter->events.head;
+ size_t length = 0;
+
+ switch (event->type)
+ {
+ case YAML_ALIAS_EVENT:
+ length += emitter->anchor_data.anchor_length;
+ break;
+
+ case YAML_SCALAR_EVENT:
+ if (emitter->scalar_data.multiline)
+ return 0;
+ length += emitter->anchor_data.anchor_length
+ + emitter->tag_data.handle_length
+ + emitter->tag_data.suffix_length
+ + emitter->scalar_data.length;
+ break;
+
+ case YAML_SEQUENCE_START_EVENT:
+ if (!yaml_emitter_check_empty_sequence(emitter))
+ return 0;
+ length += emitter->anchor_data.anchor_length
+ + emitter->tag_data.handle_length
+ + emitter->tag_data.suffix_length;
+ break;
+
+ case YAML_MAPPING_START_EVENT:
+ if (!yaml_emitter_check_empty_mapping(emitter))
+ return 0;
+ length += emitter->anchor_data.anchor_length
+ + emitter->tag_data.handle_length
+ + emitter->tag_data.suffix_length;
+ break;
+
+ default:
+ return 0;
+ }
+
+ if (length > 128)
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Determine an acceptable scalar style.
+ */
+
+static int
+yaml_emitter_select_scalar_style(yaml_emitter_t *emitter, yaml_event_t *event)
+{
+ yaml_scalar_style_t style = event->data.scalar.style;
+ int no_tag = (!emitter->tag_data.handle && !emitter->tag_data.suffix);
+
+ if (no_tag && !event->data.scalar.plain_implicit
+ && !event->data.scalar.quoted_implicit) {
+ return yaml_emitter_set_emitter_error(emitter,
+ "neither tag nor implicit flags are specified");
+ }
+
+ if (style == YAML_ANY_SCALAR_STYLE)
+ style = YAML_PLAIN_SCALAR_STYLE;
+
+ if (emitter->canonical)
+ style = YAML_DOUBLE_QUOTED_SCALAR_STYLE;
+
+ if (emitter->simple_key_context && emitter->scalar_data.multiline)
+ style = YAML_DOUBLE_QUOTED_SCALAR_STYLE;
+
+ if (style == YAML_PLAIN_SCALAR_STYLE)
+ {
+ if ((emitter->flow_level && !emitter->scalar_data.flow_plain_allowed)
+ || (!emitter->flow_level && !emitter->scalar_data.block_plain_allowed))
+ style = YAML_SINGLE_QUOTED_SCALAR_STYLE;
+ if (!emitter->scalar_data.length
+ && (emitter->flow_level || emitter->simple_key_context))
+ style = YAML_SINGLE_QUOTED_SCALAR_STYLE;
+ if (no_tag && !event->data.scalar.plain_implicit)
+ style = YAML_SINGLE_QUOTED_SCALAR_STYLE;
+ }
+
+ if (style == YAML_SINGLE_QUOTED_SCALAR_STYLE)
+ {
+ if (!emitter->scalar_data.single_quoted_allowed)
+ style = YAML_DOUBLE_QUOTED_SCALAR_STYLE;
+ }
+
+ if (style == YAML_LITERAL_SCALAR_STYLE || style == YAML_FOLDED_SCALAR_STYLE)
+ {
+ if (!emitter->scalar_data.block_allowed
+ || emitter->flow_level || emitter->simple_key_context)
+ style = YAML_DOUBLE_QUOTED_SCALAR_STYLE;
+ }
+
+ if (no_tag && !event->data.scalar.quoted_implicit
+ && style != YAML_PLAIN_SCALAR_STYLE)
+ {
+ emitter->tag_data.handle = (yaml_char_t *)"!";
+ emitter->tag_data.handle_length = 1;
+ }
+
+ emitter->scalar_data.style = style;
+
+ return 1;
+}
+
+/*
+ * Write an achor.
+ */
+
+static int
+yaml_emitter_process_anchor(yaml_emitter_t *emitter)
+{
+ if (!emitter->anchor_data.anchor)
+ return 1;
+
+ if (!yaml_emitter_write_indicator(emitter,
+ (emitter->anchor_data.alias ? "*" : "&"), 1, 0, 0))
+ return 0;
+
+ return yaml_emitter_write_anchor(emitter,
+ emitter->anchor_data.anchor, emitter->anchor_data.anchor_length);
+}
+
+/*
+ * Write a tag.
+ */
+
+static int
+yaml_emitter_process_tag(yaml_emitter_t *emitter)
+{
+ if (!emitter->tag_data.handle && !emitter->tag_data.suffix)
+ return 1;
+
+ if (emitter->tag_data.handle)
+ {
+ if (!yaml_emitter_write_tag_handle(emitter, emitter->tag_data.handle,
+ emitter->tag_data.handle_length))
+ return 0;
+ if (emitter->tag_data.suffix) {
+ if (!yaml_emitter_write_tag_content(emitter, emitter->tag_data.suffix,
+ emitter->tag_data.suffix_length, 0))
+ return 0;
+ }
+ }
+ else
+ {
+ if (!yaml_emitter_write_indicator(emitter, "!<", 1, 0, 0))
+ return 0;
+ if (!yaml_emitter_write_tag_content(emitter, emitter->tag_data.suffix,
+ emitter->tag_data.suffix_length, 0))
+ return 0;
+ if (!yaml_emitter_write_indicator(emitter, ">", 0, 0, 0))
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Write a scalar.
+ */
+
+static int
+yaml_emitter_process_scalar(yaml_emitter_t *emitter)
+{
+ switch (emitter->scalar_data.style)
+ {
+ case YAML_PLAIN_SCALAR_STYLE:
+ return yaml_emitter_write_plain_scalar(emitter,
+ emitter->scalar_data.value, emitter->scalar_data.length,
+ !emitter->simple_key_context);
+
+ case YAML_SINGLE_QUOTED_SCALAR_STYLE:
+ return yaml_emitter_write_single_quoted_scalar(emitter,
+ emitter->scalar_data.value, emitter->scalar_data.length,
+ !emitter->simple_key_context);
+
+ case YAML_DOUBLE_QUOTED_SCALAR_STYLE:
+ return yaml_emitter_write_double_quoted_scalar(emitter,
+ emitter->scalar_data.value, emitter->scalar_data.length,
+ !emitter->simple_key_context);
+
+ case YAML_LITERAL_SCALAR_STYLE:
+ return yaml_emitter_write_literal_scalar(emitter,
+ emitter->scalar_data.value, emitter->scalar_data.length);
+
+ case YAML_FOLDED_SCALAR_STYLE:
+ return yaml_emitter_write_folded_scalar(emitter,
+ emitter->scalar_data.value, emitter->scalar_data.length);
+
+ default:
+ assert(1); /* Impossible. */
+ }
+
+ return 0;
+}
+
+/*
+ * Check if a %YAML directive is valid.
+ */
+
+static int
+yaml_emitter_analyze_version_directive(yaml_emitter_t *emitter,
+ yaml_version_directive_t version_directive)
+{
+ if (version_directive.major != 1 || version_directive.minor != 1) {
+ return yaml_emitter_set_emitter_error(emitter,
+ "incompatible %YAML directive");
+ }
+
+ return 1;
+}
+
+/*
+ * Check if a %TAG directive is valid.
+ */
+
+static int
+yaml_emitter_analyze_tag_directive(yaml_emitter_t *emitter,
+ yaml_tag_directive_t tag_directive)
+{
+ yaml_string_t handle;
+ yaml_string_t prefix;
+ size_t handle_length;
+ size_t prefix_length;
+
+ handle_length = strlen((char *)tag_directive.handle);
+ prefix_length = strlen((char *)tag_directive.prefix);
+ STRING_ASSIGN(handle, tag_directive.handle, handle_length);
+ STRING_ASSIGN(prefix, tag_directive.prefix, prefix_length);
+
+ if (handle.start == handle.end) {
+ return yaml_emitter_set_emitter_error(emitter,
+ "tag handle must not be empty");
+ }
+
+ if (handle.start[0] != '!') {
+ return yaml_emitter_set_emitter_error(emitter,
+ "tag handle must start with '!'");
+ }
+
+ if (handle.end[-1] != '!') {
+ return yaml_emitter_set_emitter_error(emitter,
+ "tag handle must end with '!'");
+ }
+
+ handle.pointer ++;
+
+ while (handle.pointer < handle.end-1) {
+ if (!IS_ALPHA(handle)) {
+ return yaml_emitter_set_emitter_error(emitter,
+ "tag handle must contain alphanumerical characters only");
+ }
+ MOVE(handle);
+ }
+
+ if (prefix.start == prefix.end) {
+ return yaml_emitter_set_emitter_error(emitter,
+ "tag prefix must not be empty");
+ }
+
+ return 1;
+}
+
+/*
+ * Check if an anchor is valid.
+ */
+
+static int
+yaml_emitter_analyze_anchor(yaml_emitter_t *emitter,
+ yaml_char_t *anchor, int alias)
+{
+ size_t anchor_length;
+ yaml_string_t string;
+
+ anchor_length = strlen((char *)anchor);
+ STRING_ASSIGN(string, anchor, anchor_length);
+
+ if (string.start == string.end) {
+ return yaml_emitter_set_emitter_error(emitter, alias ?
+ "alias value must not be empty" :
+ "anchor value must not be empty");
+ }
+
+ while (string.pointer != string.end) {
+ if (!IS_ALPHA(string)) {
+ return yaml_emitter_set_emitter_error(emitter, alias ?
+ "alias value must contain alphanumerical characters only" :
+ "anchor value must contain alphanumerical characters only");
+ }
+ MOVE(string);
+ }
+
+ emitter->anchor_data.anchor = string.start;
+ emitter->anchor_data.anchor_length = string.end - string.start;
+ emitter->anchor_data.alias = alias;
+
+ return 1;
+}
+
+/*
+ * Check if a tag is valid.
+ */
+
+static int
+yaml_emitter_analyze_tag(yaml_emitter_t *emitter,
+ yaml_char_t *tag)
+{
+ size_t tag_length;
+ yaml_string_t string;
+ yaml_tag_directive_t *tag_directive;
+
+ tag_length = strlen((char *)tag);
+ STRING_ASSIGN(string, tag, tag_length);
+
+ if (string.start == string.end) {
+ return yaml_emitter_set_emitter_error(emitter,
+ "tag value must not be empty");
+ }
+
+ for (tag_directive = emitter->tag_directives.start;
+ tag_directive != emitter->tag_directives.top; tag_directive ++) {
+ size_t prefix_length = strlen((char *)tag_directive->prefix);
+ if (prefix_length < (size_t)(string.end - string.start)
+ && strncmp((char *)tag_directive->prefix, (char *)string.start,
+ prefix_length) == 0)
+ {
+ emitter->tag_data.handle = tag_directive->handle;
+ emitter->tag_data.handle_length =
+ strlen((char *)tag_directive->handle);
+ emitter->tag_data.suffix = string.start + prefix_length;
+ emitter->tag_data.suffix_length =
+ (string.end - string.start) - prefix_length;
+ return 1;
+ }
+ }
+
+ emitter->tag_data.suffix = string.start;
+ emitter->tag_data.suffix_length = string.end - string.start;
+
+ return 1;
+}
+
+/*
+ * Check if a scalar is valid.
+ */
+
+static int
+yaml_emitter_analyze_scalar(yaml_emitter_t *emitter,
+ yaml_char_t *value, size_t length)
+{
+ yaml_string_t string;
+
+ int block_indicators = 0;
+ int flow_indicators = 0;
+ int line_breaks = 0;
+ int special_characters = 0;
+
+ int leading_space = 0;
+ int leading_break = 0;
+ int trailing_space = 0;
+ int trailing_break = 0;
+ int break_space = 0;
+ int space_break = 0;
+
+ int preceeded_by_whitespace = 0;
+ int followed_by_whitespace = 0;
+ int previous_space = 0;
+ int previous_break = 0;
+
+ STRING_ASSIGN(string, value, length);
+
+ emitter->scalar_data.value = value;
+ emitter->scalar_data.length = length;
+
+ if (string.start == string.end)
+ {
+ emitter->scalar_data.multiline = 0;
+ emitter->scalar_data.flow_plain_allowed = 0;
+ emitter->scalar_data.block_plain_allowed = 1;
+ emitter->scalar_data.single_quoted_allowed = 1;
+ emitter->scalar_data.block_allowed = 0;
+
+ return 1;
+ }
+
+ if ((CHECK_AT(string, '-', 0)
+ && CHECK_AT(string, '-', 1)
+ && CHECK_AT(string, '-', 2))
+ || (CHECK_AT(string, '.', 0)
+ && CHECK_AT(string, '.', 1)
+ && CHECK_AT(string, '.', 2))) {
+ block_indicators = 1;
+ flow_indicators = 1;
+ }
+
+ preceeded_by_whitespace = 1;
+ followed_by_whitespace = IS_BLANKZ_AT(string, WIDTH(string));
+
+ while (string.pointer != string.end)
+ {
+ if (string.start == string.pointer)
+ {
+ if (CHECK(string, '#') || CHECK(string, ',')
+ || CHECK(string, '[') || CHECK(string, ']')
+ || CHECK(string, '{') || CHECK(string, '}')
+ || CHECK(string, '&') || CHECK(string, '*')
+ || CHECK(string, '!') || CHECK(string, '|')
+ || CHECK(string, '>') || CHECK(string, '\'')
+ || CHECK(string, '"') || CHECK(string, '%')
+ || CHECK(string, '@') || CHECK(string, '`')) {
+ flow_indicators = 1;
+ block_indicators = 1;
+ }
+
+ if (CHECK(string, '?') || CHECK(string, ':')) {
+ flow_indicators = 1;
+ if (followed_by_whitespace) {
+ block_indicators = 1;
+ }
+ }
+
+ if (CHECK(string, '-') && followed_by_whitespace) {
+ flow_indicators = 1;
+ block_indicators = 1;
+ }
+ }
+ else
+ {
+ if (CHECK(string, ',') || CHECK(string, '?')
+ || CHECK(string, '[') || CHECK(string, ']')
+ || CHECK(string, '{') || CHECK(string, '}')) {
+ flow_indicators = 1;
+ }
+
+ if (CHECK(string, ':')) {
+ flow_indicators = 1;
+ if (followed_by_whitespace) {
+ block_indicators = 1;
+ }
+ }
+
+ if (CHECK(string, '#') && preceeded_by_whitespace) {
+ flow_indicators = 1;
+ block_indicators = 1;
+ }
+ }
+
+ if (!IS_PRINTABLE(string)
+ || (!IS_ASCII(string) && !emitter->unicode)) {
+ special_characters = 1;
+ }
+
+ if (IS_BREAK(string)) {
+ line_breaks = 1;
+ }
+
+ if (IS_SPACE(string))
+ {
+ if (string.start == string.pointer) {
+ leading_space = 1;
+ }
+ if (string.pointer+WIDTH(string) == string.end) {
+ trailing_space = 1;
+ }
+ if (previous_break) {
+ break_space = 1;
+ }
+ previous_space = 1;
+ previous_break = 0;
+ }
+ else if (IS_BREAK(string))
+ {
+ if (string.start == string.pointer) {
+ leading_break = 1;
+ }
+ if (string.pointer+WIDTH(string) == string.end) {
+ trailing_break = 1;
+ }
+ if (previous_space) {
+ space_break = 1;
+ }
+ previous_space = 0;
+ previous_break = 1;
+ }
+ else
+ {
+ previous_space = 0;
+ previous_break = 0;
+ }
+
+ preceeded_by_whitespace = IS_BLANKZ(string);
+ MOVE(string);
+ if (string.pointer != string.end) {
+ followed_by_whitespace = IS_BLANKZ_AT(string, WIDTH(string));
+ }
+ }
+
+ emitter->scalar_data.multiline = line_breaks;
+
+ emitter->scalar_data.flow_plain_allowed = 1;
+ emitter->scalar_data.block_plain_allowed = 1;
+ emitter->scalar_data.single_quoted_allowed = 1;
+ emitter->scalar_data.block_allowed = 1;
+
+ if (leading_space || leading_break || trailing_space || trailing_break) {
+ emitter->scalar_data.flow_plain_allowed = 0;
+ emitter->scalar_data.block_plain_allowed = 0;
+ }
+
+ if (trailing_space) {
+ emitter->scalar_data.block_allowed = 0;
+ }
+
+ if (break_space) {
+ emitter->scalar_data.flow_plain_allowed = 0;
+ emitter->scalar_data.block_plain_allowed = 0;
+ emitter->scalar_data.single_quoted_allowed = 0;
+ }
+
+ if (space_break || special_characters) {
+ emitter->scalar_data.flow_plain_allowed = 0;
+ emitter->scalar_data.block_plain_allowed = 0;
+ emitter->scalar_data.single_quoted_allowed = 0;
+ emitter->scalar_data.block_allowed = 0;
+ }
+
+ if (line_breaks) {
+ emitter->scalar_data.flow_plain_allowed = 0;
+ emitter->scalar_data.block_plain_allowed = 0;
+ }
+
+ if (flow_indicators) {
+ emitter->scalar_data.flow_plain_allowed = 0;
+ }
+
+ if (block_indicators) {
+ emitter->scalar_data.block_plain_allowed = 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Check if the event data is valid.
+ */
+
+static int
+yaml_emitter_analyze_event(yaml_emitter_t *emitter,
+ yaml_event_t *event)
+{
+ emitter->anchor_data.anchor = NULL;
+ emitter->anchor_data.anchor_length = 0;
+ emitter->tag_data.handle = NULL;
+ emitter->tag_data.handle_length = 0;
+ emitter->tag_data.suffix = NULL;
+ emitter->tag_data.suffix_length = 0;
+ emitter->scalar_data.value = NULL;
+ emitter->scalar_data.length = 0;
+
+ switch (event->type)
+ {
+ case YAML_ALIAS_EVENT:
+ if (!yaml_emitter_analyze_anchor(emitter,
+ event->data.alias.anchor, 1))
+ return 0;
+ return 1;
+
+ case YAML_SCALAR_EVENT:
+ if (event->data.scalar.anchor) {
+ if (!yaml_emitter_analyze_anchor(emitter,
+ event->data.scalar.anchor, 0))
+ return 0;
+ }
+ if (event->data.scalar.tag && (emitter->canonical ||
+ (!event->data.scalar.plain_implicit
+ && !event->data.scalar.quoted_implicit))) {
+ if (!yaml_emitter_analyze_tag(emitter, event->data.scalar.tag))
+ return 0;
+ }
+ if (!yaml_emitter_analyze_scalar(emitter,
+ event->data.scalar.value, event->data.scalar.length))
+ return 0;
+ return 1;
+
+ case YAML_SEQUENCE_START_EVENT:
+ if (event->data.sequence_start.anchor) {
+ if (!yaml_emitter_analyze_anchor(emitter,
+ event->data.sequence_start.anchor, 0))
+ return 0;
+ }
+ if (event->data.sequence_start.tag && (emitter->canonical ||
+ !event->data.sequence_start.implicit)) {
+ if (!yaml_emitter_analyze_tag(emitter,
+ event->data.sequence_start.tag))
+ return 0;
+ }
+ return 1;
+
+ case YAML_MAPPING_START_EVENT:
+ if (event->data.mapping_start.anchor) {
+ if (!yaml_emitter_analyze_anchor(emitter,
+ event->data.mapping_start.anchor, 0))
+ return 0;
+ }
+ if (event->data.mapping_start.tag && (emitter->canonical ||
+ !event->data.mapping_start.implicit)) {
+ if (!yaml_emitter_analyze_tag(emitter,
+ event->data.mapping_start.tag))
+ return 0;
+ }
+ return 1;
+
+ default:
+ return 1;
+ }
+}
+
+/*
+ * Write the BOM character.
+ */
+
+static int
+yaml_emitter_write_bom(yaml_emitter_t *emitter)
+{
+ if (!FLUSH(emitter)) return 0;
+
+ *(emitter->buffer.pointer++) = (yaml_char_t) '\xEF';
+ *(emitter->buffer.pointer++) = (yaml_char_t) '\xBB';
+ *(emitter->buffer.pointer++) = (yaml_char_t) '\xBF';
+
+ return 1;
+}
+
+static int
+yaml_emitter_write_indent(yaml_emitter_t *emitter)
+{
+ int indent = (emitter->indent >= 0) ? emitter->indent : 0;
+
+ if (!emitter->indention || emitter->column > indent
+ || (emitter->column == indent && !emitter->whitespace)) {
+ if (!PUT_BREAK(emitter)) return 0;
+ }
+
+ while (emitter->column < indent) {
+ if (!PUT(emitter, ' ')) return 0;
+ }
+
+ emitter->whitespace = 1;
+ emitter->indention = 1;
+
+ return 1;
+}
+
+static int
+yaml_emitter_write_indicator(yaml_emitter_t *emitter,
+ const char *indicator, int need_whitespace,
+ int is_whitespace, int is_indention)
+{
+ size_t indicator_length;
+ yaml_string_t string;
+
+ indicator_length = strlen(indicator);
+ STRING_ASSIGN(string, (yaml_char_t *)indicator, indicator_length);
+
+ if (need_whitespace && !emitter->whitespace) {
+ if (!PUT(emitter, ' ')) return 0;
+ }
+
+ while (string.pointer != string.end) {
+ if (!WRITE(emitter, string)) return 0;
+ }
+
+ emitter->whitespace = is_whitespace;
+ emitter->indention = (emitter->indention && is_indention);
+ emitter->open_ended = 0;
+
+ return 1;
+}
+
+static int
+yaml_emitter_write_anchor(yaml_emitter_t *emitter,
+ yaml_char_t *value, size_t length)
+{
+ yaml_string_t string;
+ STRING_ASSIGN(string, value, length);
+
+ while (string.pointer != string.end) {
+ if (!WRITE(emitter, string)) return 0;
+ }
+
+ emitter->whitespace = 0;
+ emitter->indention = 0;
+
+ return 1;
+}
+
+static int
+yaml_emitter_write_tag_handle(yaml_emitter_t *emitter,
+ yaml_char_t *value, size_t length)
+{
+ yaml_string_t string;
+ STRING_ASSIGN(string, value, length);
+
+ if (!emitter->whitespace) {
+ if (!PUT(emitter, ' ')) return 0;
+ }
+
+ while (string.pointer != string.end) {
+ if (!WRITE(emitter, string)) return 0;
+ }
+
+ emitter->whitespace = 0;
+ emitter->indention = 0;
+
+ return 1;
+}
+
+static int
+yaml_emitter_write_tag_content(yaml_emitter_t *emitter,
+ yaml_char_t *value, size_t length,
+ int need_whitespace)
+{
+ yaml_string_t string;
+ STRING_ASSIGN(string, value, length);
+
+ if (need_whitespace && !emitter->whitespace) {
+ if (!PUT(emitter, ' ')) return 0;
+ }
+
+ while (string.pointer != string.end) {
+ if (IS_ALPHA(string)
+ || CHECK(string, ';') || CHECK(string, '/')
+ || CHECK(string, '?') || CHECK(string, ':')
+ || CHECK(string, '@') || CHECK(string, '&')
+ || CHECK(string, '=') || CHECK(string, '+')
+ || CHECK(string, '$') || CHECK(string, ',')
+ || CHECK(string, '_') || CHECK(string, '.')
+ || CHECK(string, '~') || CHECK(string, '*')
+ || CHECK(string, '\'') || CHECK(string, '(')
+ || CHECK(string, ')') || CHECK(string, '[')
+ || CHECK(string, ']')) {
+ if (!WRITE(emitter, string)) return 0;
+ }
+ else {
+ int width = WIDTH(string);
+ unsigned int value;
+ while (width --) {
+ value = *(string.pointer++);
+ if (!PUT(emitter, '%')) return 0;
+ if (!PUT(emitter, (value >> 4)
+ + ((value >> 4) < 10 ? '0' : 'A' - 10)))
+ return 0;
+ if (!PUT(emitter, (value & 0x0F)
+ + ((value & 0x0F) < 10 ? '0' : 'A' - 10)))
+ return 0;
+ }
+ }
+ }
+
+ emitter->whitespace = 0;
+ emitter->indention = 0;
+
+ return 1;
+}
+
+static int
+yaml_emitter_write_plain_scalar(yaml_emitter_t *emitter,
+ yaml_char_t *value, size_t length, int allow_breaks)
+{
+ yaml_string_t string;
+ int spaces = 0;
+ int breaks = 0;
+
+ STRING_ASSIGN(string, value, length);
+
+ if (!emitter->whitespace) {
+ if (!PUT(emitter, ' ')) return 0;
+ }
+
+ while (string.pointer != string.end)
+ {
+ if (IS_SPACE(string))
+ {
+ if (allow_breaks && !spaces
+ && emitter->column > emitter->best_width
+ && !IS_SPACE_AT(string, 1)) {
+ if (!yaml_emitter_write_indent(emitter)) return 0;
+ MOVE(string);
+ }
+ else {
+ if (!WRITE(emitter, string)) return 0;
+ }
+ spaces = 1;
+ }
+ else if (IS_BREAK(string))
+ {
+ if (!breaks && CHECK(string, '\n')) {
+ if (!PUT_BREAK(emitter)) return 0;
+ }
+ if (!WRITE_BREAK(emitter, string)) return 0;
+ emitter->indention = 1;
+ breaks = 1;
+ }
+ else
+ {
+ if (breaks) {
+ if (!yaml_emitter_write_indent(emitter)) return 0;
+ }
+ if (!WRITE(emitter, string)) return 0;
+ emitter->indention = 0;
+ spaces = 0;
+ breaks = 0;
+ }
+ }
+
+ emitter->whitespace = 0;
+ emitter->indention = 0;
+ if (emitter->root_context)
+ {
+ emitter->open_ended = 1;
+ }
+
+ return 1;
+}
+
+static int
+yaml_emitter_write_single_quoted_scalar(yaml_emitter_t *emitter,
+ yaml_char_t *value, size_t length, int allow_breaks)
+{
+ yaml_string_t string;
+ int spaces = 0;
+ int breaks = 0;
+
+ STRING_ASSIGN(string, value, length);
+
+ if (!yaml_emitter_write_indicator(emitter, "'", 1, 0, 0))
+ return 0;
+
+ while (string.pointer != string.end)
+ {
+ if (IS_SPACE(string))
+ {
+ if (allow_breaks && !spaces
+ && emitter->column > emitter->best_width
+ && string.pointer != string.start
+ && string.pointer != string.end - 1
+ && !IS_SPACE_AT(string, 1)) {
+ if (!yaml_emitter_write_indent(emitter)) return 0;
+ MOVE(string);
+ }
+ else {
+ if (!WRITE(emitter, string)) return 0;
+ }
+ spaces = 1;
+ }
+ else if (IS_BREAK(string))
+ {
+ if (!breaks && CHECK(string, '\n')) {
+ if (!PUT_BREAK(emitter)) return 0;
+ }
+ if (!WRITE_BREAK(emitter, string)) return 0;
+ emitter->indention = 1;
+ breaks = 1;
+ }
+ else
+ {
+ if (breaks) {
+ if (!yaml_emitter_write_indent(emitter)) return 0;
+ }
+ if (CHECK(string, '\'')) {
+ if (!PUT(emitter, '\'')) return 0;
+ }
+ if (!WRITE(emitter, string)) return 0;
+ emitter->indention = 0;
+ spaces = 0;
+ breaks = 0;
+ }
+ }
+
+ if (!yaml_emitter_write_indicator(emitter, "'", 0, 0, 0))
+ return 0;
+
+ emitter->whitespace = 0;
+ emitter->indention = 0;
+
+ return 1;
+}
+
+static int
+yaml_emitter_write_double_quoted_scalar(yaml_emitter_t *emitter,
+ yaml_char_t *value, size_t length, int allow_breaks)
+{
+ yaml_string_t string;
+ int spaces = 0;
+
+ STRING_ASSIGN(string, value, length);
+
+ if (!yaml_emitter_write_indicator(emitter, "\"", 1, 0, 0))
+ return 0;
+
+ while (string.pointer != string.end)
+ {
+ if (!IS_PRINTABLE(string) || (!emitter->unicode && !IS_ASCII(string))
+ || IS_BOM(string) || IS_BREAK(string)
+ || CHECK(string, '"') || CHECK(string, '\\'))
+ {
+ unsigned char octet;
+ unsigned int width;
+ unsigned int value;
+ int k;
+
+ octet = string.pointer[0];
+ width = (octet & 0x80) == 0x00 ? 1 :
+ (octet & 0xE0) == 0xC0 ? 2 :
+ (octet & 0xF0) == 0xE0 ? 3 :
+ (octet & 0xF8) == 0xF0 ? 4 : 0;
+ value = (octet & 0x80) == 0x00 ? octet & 0x7F :
+ (octet & 0xE0) == 0xC0 ? octet & 0x1F :
+ (octet & 0xF0) == 0xE0 ? octet & 0x0F :
+ (octet & 0xF8) == 0xF0 ? octet & 0x07 : 0;
+ for (k = 1; k < (int)width; k ++) {
+ octet = string.pointer[k];
+ value = (value << 6) + (octet & 0x3F);
+ }
+ string.pointer += width;
+
+ if (!PUT(emitter, '\\')) return 0;
+
+ switch (value)
+ {
+ case 0x00:
+ if (!PUT(emitter, '0')) return 0;
+ break;
+
+ case 0x07:
+ if (!PUT(emitter, 'a')) return 0;
+ break;
+
+ case 0x08:
+ if (!PUT(emitter, 'b')) return 0;
+ break;
+
+ case 0x09:
+ if (!PUT(emitter, 't')) return 0;
+ break;
+
+ case 0x0A:
+ if (!PUT(emitter, 'n')) return 0;
+ break;
+
+ case 0x0B:
+ if (!PUT(emitter, 'v')) return 0;
+ break;
+
+ case 0x0C:
+ if (!PUT(emitter, 'f')) return 0;
+ break;
+
+ case 0x0D:
+ if (!PUT(emitter, 'r')) return 0;
+ break;
+
+ case 0x1B:
+ if (!PUT(emitter, 'e')) return 0;
+ break;
+
+ case 0x22:
+ if (!PUT(emitter, '\"')) return 0;
+ break;
+
+ case 0x5C:
+ if (!PUT(emitter, '\\')) return 0;
+ break;
+
+ case 0x85:
+ if (!PUT(emitter, 'N')) return 0;
+ break;
+
+ case 0xA0:
+ if (!PUT(emitter, '_')) return 0;
+ break;
+
+ case 0x2028:
+ if (!PUT(emitter, 'L')) return 0;
+ break;
+
+ case 0x2029:
+ if (!PUT(emitter, 'P')) return 0;
+ break;
+
+ default:
+ if (value <= 0xFF) {
+ if (!PUT(emitter, 'x')) return 0;
+ width = 2;
+ }
+ else if (value <= 0xFFFF) {
+ if (!PUT(emitter, 'u')) return 0;
+ width = 4;
+ }
+ else {
+ if (!PUT(emitter, 'U')) return 0;
+ width = 8;
+ }
+ for (k = (width-1)*4; k >= 0; k -= 4) {
+ int digit = (value >> k) & 0x0F;
+ if (!PUT(emitter, digit + (digit < 10 ? '0' : 'A'-10)))
+ return 0;
+ }
+ }
+ spaces = 0;
+ }
+ else if (IS_SPACE(string))
+ {
+ if (allow_breaks && !spaces
+ && emitter->column > emitter->best_width
+ && string.pointer != string.start
+ && string.pointer != string.end - 1) {
+ if (!yaml_emitter_write_indent(emitter)) return 0;
+ if (IS_SPACE_AT(string, 1)) {
+ if (!PUT(emitter, '\\')) return 0;
+ }
+ MOVE(string);
+ }
+ else {
+ if (!WRITE(emitter, string)) return 0;
+ }
+ spaces = 1;
+ }
+ else
+ {
+ if (!WRITE(emitter, string)) return 0;
+ spaces = 0;
+ }
+ }
+
+ if (!yaml_emitter_write_indicator(emitter, "\"", 0, 0, 0))
+ return 0;
+
+ emitter->whitespace = 0;
+ emitter->indention = 0;
+
+ return 1;
+}
+
+static int
+yaml_emitter_write_block_scalar_hints(yaml_emitter_t *emitter,
+ yaml_string_t string)
+{
+ char indent_hint[2];
+ const char *chomp_hint = NULL;
+
+ if (IS_SPACE(string) || IS_BREAK(string))
+ {
+ indent_hint[0] = '0' + (char)emitter->best_indent;
+ indent_hint[1] = '\0';
+ if (!yaml_emitter_write_indicator(emitter, indent_hint, 0, 0, 0))
+ return 0;
+ }
+
+ emitter->open_ended = 0;
+
+ string.pointer = string.end;
+ if (string.start == string.pointer)
+ {
+ chomp_hint = "-";
+ }
+ else
+ {
+ do {
+ string.pointer --;
+ } while ((*string.pointer & 0xC0) == 0x80);
+ if (!IS_BREAK(string))
+ {
+ chomp_hint = "-";
+ }
+ else if (string.start == string.pointer)
+ {
+ chomp_hint = "+";
+ emitter->open_ended = 1;
+ }
+ else
+ {
+ do {
+ string.pointer --;
+ } while ((*string.pointer & 0xC0) == 0x80);
+ if (IS_BREAK(string))
+ {
+ chomp_hint = "+";
+ emitter->open_ended = 1;
+ }
+ }
+ }
+
+ if (chomp_hint)
+ {
+ if (!yaml_emitter_write_indicator(emitter, chomp_hint, 0, 0, 0))
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+yaml_emitter_write_literal_scalar(yaml_emitter_t *emitter,
+ yaml_char_t *value, size_t length)
+{
+ yaml_string_t string;
+ int breaks = 1;
+
+ STRING_ASSIGN(string, value, length);
+
+ if (!yaml_emitter_write_indicator(emitter, "|", 1, 0, 0))
+ return 0;
+ if (!yaml_emitter_write_block_scalar_hints(emitter, string))
+ return 0;
+ if (!PUT_BREAK(emitter)) return 0;
+ emitter->indention = 1;
+ emitter->whitespace = 1;
+
+ while (string.pointer != string.end)
+ {
+ if (IS_BREAK(string))
+ {
+ if (!WRITE_BREAK(emitter, string)) return 0;
+ emitter->indention = 1;
+ breaks = 1;
+ }
+ else
+ {
+ if (breaks) {
+ if (!yaml_emitter_write_indent(emitter)) return 0;
+ }
+ if (!WRITE(emitter, string)) return 0;
+ emitter->indention = 0;
+ breaks = 0;
+ }
+ }
+
+ return 1;
+}
+
+static int
+yaml_emitter_write_folded_scalar(yaml_emitter_t *emitter,
+ yaml_char_t *value, size_t length)
+{
+ yaml_string_t string;
+ int breaks = 1;
+ int leading_spaces = 1;
+
+ STRING_ASSIGN(string, value, length);
+
+ if (!yaml_emitter_write_indicator(emitter, ">", 1, 0, 0))
+ return 0;
+ if (!yaml_emitter_write_block_scalar_hints(emitter, string))
+ return 0;
+ if (!PUT_BREAK(emitter)) return 0;
+ emitter->indention = 1;
+ emitter->whitespace = 1;
+
+ while (string.pointer != string.end)
+ {
+ if (IS_BREAK(string))
+ {
+ if (!breaks && !leading_spaces && CHECK(string, '\n')) {
+ int k = 0;
+ while (IS_BREAK_AT(string, k)) {
+ k += WIDTH_AT(string, k);
+ }
+ if (!IS_BLANKZ_AT(string, k)) {
+ if (!PUT_BREAK(emitter)) return 0;
+ }
+ }
+ if (!WRITE_BREAK(emitter, string)) return 0;
+ emitter->indention = 1;
+ breaks = 1;
+ }
+ else
+ {
+ if (breaks) {
+ if (!yaml_emitter_write_indent(emitter)) return 0;
+ leading_spaces = IS_BLANK(string);
+ }
+ if (!breaks && IS_SPACE(string) && !IS_SPACE_AT(string, 1)
+ && emitter->column > emitter->best_width) {
+ if (!yaml_emitter_write_indent(emitter)) return 0;
+ MOVE(string);
+ }
+ else {
+ if (!WRITE(emitter, string)) return 0;
+ }
+ emitter->indention = 0;
+ breaks = 0;
+ }
+ }
+
+ return 1;
+}
+
diff --git a/ext/psych/yaml/loader.c b/ext/psych/yaml/loader.c
new file mode 100644
index 0000000..cb3ea93
--- /dev/null
+++ b/ext/psych/yaml/loader.c
@@ -0,0 +1,459 @@
+
+#include "yaml_private.h"
+
+/*
+ * API functions.
+ */
+
+YAML_DECLARE(int)
+yaml_parser_load(yaml_parser_t *parser, yaml_document_t *document);
+
+/*
+ * Error handling.
+ */
+
+static int
+yaml_parser_set_composer_error(yaml_parser_t *parser,
+ const char *problem, yaml_mark_t problem_mark);
+
+static int
+yaml_parser_set_composer_error_context(yaml_parser_t *parser,
+ const char *context, yaml_mark_t context_mark,
+ const char *problem, yaml_mark_t problem_mark);
+
+
+/*
+ * Alias handling.
+ */
+
+static int
+yaml_parser_register_anchor(yaml_parser_t *parser,
+ int index, yaml_char_t *anchor);
+
+/*
+ * Clean up functions.
+ */
+
+static void
+yaml_parser_delete_aliases(yaml_parser_t *parser);
+
+/*
+ * Composer functions.
+ */
+
+static int
+yaml_parser_load_document(yaml_parser_t *parser, yaml_event_t *first_event);
+
+static int
+yaml_parser_load_node(yaml_parser_t *parser, yaml_event_t *first_event);
+
+static int
+yaml_parser_load_alias(yaml_parser_t *parser, yaml_event_t *first_event);
+
+static int
+yaml_parser_load_scalar(yaml_parser_t *parser, yaml_event_t *first_event);
+
+static int
+yaml_parser_load_sequence(yaml_parser_t *parser, yaml_event_t *first_event);
+
+static int
+yaml_parser_load_mapping(yaml_parser_t *parser, yaml_event_t *first_event);
+
+/*
+ * Load the next document of the stream.
+ */
+
+YAML_DECLARE(int)
+yaml_parser_load(yaml_parser_t *parser, yaml_document_t *document)
+{
+ yaml_event_t event;
+
+ assert(parser); /* Non-NULL parser object is expected. */
+ assert(document); /* Non-NULL document object is expected. */
+
+ memset(document, 0, sizeof(yaml_document_t));
+ if (!STACK_INIT(parser, document->nodes, INITIAL_STACK_SIZE))
+ goto error;
+
+ if (!parser->stream_start_produced) {
+ if (!yaml_parser_parse(parser, &event)) goto error;
+ assert(event.type == YAML_STREAM_START_EVENT);
+ /* STREAM-START is expected. */
+ }
+
+ if (parser->stream_end_produced) {
+ return 1;
+ }
+
+ if (!yaml_parser_parse(parser, &event)) goto error;
+ if (event.type == YAML_STREAM_END_EVENT) {
+ return 1;
+ }
+
+ if (!STACK_INIT(parser, parser->aliases, INITIAL_STACK_SIZE))
+ goto error;
+
+ parser->document = document;
+
+ if (!yaml_parser_load_document(parser, &event)) goto error;
+
+ yaml_parser_delete_aliases(parser);
+ parser->document = NULL;
+
+ return 1;
+
+error:
+
+ yaml_parser_delete_aliases(parser);
+ yaml_document_delete(document);
+ parser->document = NULL;
+
+ return 0;
+}
+
+/*
+ * Set composer error.
+ */
+
+static int
+yaml_parser_set_composer_error(yaml_parser_t *parser,
+ const char *problem, yaml_mark_t problem_mark)
+{
+ parser->error = YAML_COMPOSER_ERROR;
+ parser->problem = problem;
+ parser->problem_mark = problem_mark;
+
+ return 0;
+}
+
+/*
+ * Set composer error with context.
+ */
+
+static int
+yaml_parser_set_composer_error_context(yaml_parser_t *parser,
+ const char *context, yaml_mark_t context_mark,
+ const char *problem, yaml_mark_t problem_mark)
+{
+ parser->error = YAML_COMPOSER_ERROR;
+ parser->context = context;
+ parser->context_mark = context_mark;
+ parser->problem = problem;
+ parser->problem_mark = problem_mark;
+
+ return 0;
+}
+
+/*
+ * Delete the stack of aliases.
+ */
+
+static void
+yaml_parser_delete_aliases(yaml_parser_t *parser)
+{
+ while (!STACK_EMPTY(parser, parser->aliases)) {
+ yaml_free(POP(parser, parser->aliases).anchor);
+ }
+ STACK_DEL(parser, parser->aliases);
+}
+
+/*
+ * Compose a document object.
+ */
+
+static int
+yaml_parser_load_document(yaml_parser_t *parser, yaml_event_t *first_event)
+{
+ yaml_event_t event;
+
+ assert(first_event->type == YAML_DOCUMENT_START_EVENT);
+ /* DOCUMENT-START is expected. */
+
+ parser->document->version_directive
+ = first_event->data.document_start.version_directive;
+ parser->document->tag_directives.start
+ = first_event->data.document_start.tag_directives.start;
+ parser->document->tag_directives.end
+ = first_event->data.document_start.tag_directives.end;
+ parser->document->start_implicit
+ = first_event->data.document_start.implicit;
+ parser->document->start_mark = first_event->start_mark;
+
+ if (!yaml_parser_parse(parser, &event)) return 0;
+
+ if (!yaml_parser_load_node(parser, &event)) return 0;
+
+ if (!yaml_parser_parse(parser, &event)) return 0;
+ assert(event.type == YAML_DOCUMENT_END_EVENT);
+ /* DOCUMENT-END is expected. */
+
+ parser->document->end_implicit = event.data.document_end.implicit;
+ parser->document->end_mark = event.end_mark;
+
+ return 1;
+}
+
+/*
+ * Compose a node.
+ */
+
+static int
+yaml_parser_load_node(yaml_parser_t *parser, yaml_event_t *first_event)
+{
+ switch (first_event->type) {
+ case YAML_ALIAS_EVENT:
+ return yaml_parser_load_alias(parser, first_event);
+ case YAML_SCALAR_EVENT:
+ return yaml_parser_load_scalar(parser, first_event);
+ case YAML_SEQUENCE_START_EVENT:
+ return yaml_parser_load_sequence(parser, first_event);
+ case YAML_MAPPING_START_EVENT:
+ return yaml_parser_load_mapping(parser, first_event);
+ default:
+ assert(0); /* Could not happen. */
+ return 0;
+ }
+
+ return 0;
+}
+
+/*
+ * Add an anchor.
+ */
+
+static int
+yaml_parser_register_anchor(yaml_parser_t *parser,
+ int index, yaml_char_t *anchor)
+{
+ yaml_alias_data_t data;
+ yaml_alias_data_t *alias_data;
+
+ if (!anchor) return 1;
+
+ data.anchor = anchor;
+ data.index = index;
+ data.mark = parser->document->nodes.start[index-1].start_mark;
+
+ for (alias_data = parser->aliases.start;
+ alias_data != parser->aliases.top; alias_data ++) {
+ if (strcmp((char *)alias_data->anchor, (char *)anchor) == 0) {
+ yaml_free(anchor);
+ return yaml_parser_set_composer_error_context(parser,
+ "found duplicate anchor; first occurence",
+ alias_data->mark, "second occurence", data.mark);
+ }
+ }
+
+ if (!PUSH(parser, parser->aliases, data)) {
+ yaml_free(anchor);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Compose a node corresponding to an alias.
+ */
+
+static int
+yaml_parser_load_alias(yaml_parser_t *parser, yaml_event_t *first_event)
+{
+ yaml_char_t *anchor = first_event->data.alias.anchor;
+ yaml_alias_data_t *alias_data;
+
+ for (alias_data = parser->aliases.start;
+ alias_data != parser->aliases.top; alias_data ++) {
+ if (strcmp((char *)alias_data->anchor, (char *)anchor) == 0) {
+ yaml_free(anchor);
+ return alias_data->index;
+ }
+ }
+
+ yaml_free(anchor);
+ return yaml_parser_set_composer_error(parser, "found undefined alias",
+ first_event->start_mark);
+}
+
+/*
+ * Compose a scalar node.
+ */
+
+static int
+yaml_parser_load_scalar(yaml_parser_t *parser, yaml_event_t *first_event)
+{
+ yaml_node_t node;
+ ptrdiff_t node_index;
+ int index;
+ yaml_char_t *tag = first_event->data.scalar.tag;
+
+ if (!STACK_LIMIT(parser, parser->document->nodes, INT_MAX-1)) goto error;
+
+ if (!tag || strcmp((char *)tag, "!") == 0) {
+ yaml_free(tag);
+ tag = yaml_strdup((yaml_char_t *)YAML_DEFAULT_SCALAR_TAG);
+ if (!tag) goto error;
+ }
+
+ SCALAR_NODE_INIT(node, tag, first_event->data.scalar.value,
+ first_event->data.scalar.length, first_event->data.scalar.style,
+ first_event->start_mark, first_event->end_mark);
+
+ if (!PUSH(parser, parser->document->nodes, node)) goto error;
+
+ node_index = parser->document->nodes.top - parser->document->nodes.start;
+#if PTRDIFF_MAX > INT_MAX
+ if (node_index > INT_MAX) goto error;
+#endif
+ index = (int)node_index;
+
+ if (!yaml_parser_register_anchor(parser, index,
+ first_event->data.scalar.anchor)) return 0;
+
+ return index;
+
+error:
+ yaml_free(tag);
+ yaml_free(first_event->data.scalar.anchor);
+ yaml_free(first_event->data.scalar.value);
+ return 0;
+}
+
+/*
+ * Compose a sequence node.
+ */
+
+static int
+yaml_parser_load_sequence(yaml_parser_t *parser, yaml_event_t *first_event)
+{
+ yaml_event_t event;
+ yaml_node_t node;
+ struct {
+ yaml_node_item_t *start;
+ yaml_node_item_t *end;
+ yaml_node_item_t *top;
+ } items = { NULL, NULL, NULL };
+ int index, item_index;
+ ptrdiff_t node_index;
+ yaml_char_t *tag = first_event->data.sequence_start.tag;
+
+ if (!STACK_LIMIT(parser, parser->document->nodes, INT_MAX-1)) goto error;
+
+ if (!tag || strcmp((char *)tag, "!") == 0) {
+ yaml_free(tag);
+ tag = yaml_strdup((yaml_char_t *)YAML_DEFAULT_SEQUENCE_TAG);
+ if (!tag) goto error;
+ }
+
+ if (!STACK_INIT(parser, items, INITIAL_STACK_SIZE)) goto error;
+
+ SEQUENCE_NODE_INIT(node, tag, items.start, items.end,
+ first_event->data.sequence_start.style,
+ first_event->start_mark, first_event->end_mark);
+
+ if (!PUSH(parser, parser->document->nodes, node)) goto error;
+
+ node_index = parser->document->nodes.top - parser->document->nodes.start;
+#if PTRDIFF_MAX > INT_MAX
+ if (node_index > INT_MAX) goto error;
+#endif
+ index = (int)node_index;
+
+ if (!yaml_parser_register_anchor(parser, index,
+ first_event->data.sequence_start.anchor)) return 0;
+
+ if (!yaml_parser_parse(parser, &event)) return 0;
+
+ while (event.type != YAML_SEQUENCE_END_EVENT) {
+ if (!STACK_LIMIT(parser,
+ parser->document->nodes.start[index-1].data.sequence.items,
+ INT_MAX-1)) return 0;
+ item_index = yaml_parser_load_node(parser, &event);
+ if (!item_index) return 0;
+ if (!PUSH(parser,
+ parser->document->nodes.start[index-1].data.sequence.items,
+ item_index)) return 0;
+ if (!yaml_parser_parse(parser, &event)) return 0;
+ }
+
+ parser->document->nodes.start[index-1].end_mark = event.end_mark;
+
+ return index;
+
+error:
+ yaml_free(tag);
+ yaml_free(first_event->data.sequence_start.anchor);
+ return 0;
+}
+
+/*
+ * Compose a mapping node.
+ */
+
+static int
+yaml_parser_load_mapping(yaml_parser_t *parser, yaml_event_t *first_event)
+{
+ yaml_event_t event;
+ yaml_node_t node;
+ struct {
+ yaml_node_pair_t *start;
+ yaml_node_pair_t *end;
+ yaml_node_pair_t *top;
+ } pairs = { NULL, NULL, NULL };
+ int index;
+ ptrdiff_t node_index;
+ yaml_node_pair_t pair;
+ yaml_char_t *tag = first_event->data.mapping_start.tag;
+
+ if (!STACK_LIMIT(parser, parser->document->nodes, INT_MAX-1)) goto error;
+
+ if (!tag || strcmp((char *)tag, "!") == 0) {
+ yaml_free(tag);
+ tag = yaml_strdup((yaml_char_t *)YAML_DEFAULT_MAPPING_TAG);
+ if (!tag) goto error;
+ }
+
+ if (!STACK_INIT(parser, pairs, INITIAL_STACK_SIZE)) goto error;
+
+ MAPPING_NODE_INIT(node, tag, pairs.start, pairs.end,
+ first_event->data.mapping_start.style,
+ first_event->start_mark, first_event->end_mark);
+
+ if (!PUSH(parser, parser->document->nodes, node)) goto error;
+
+ node_index = parser->document->nodes.top - parser->document->nodes.start;
+#if PTRDIFF_MAX > INT_MAX
+ if (node_index > INT_MAX) goto error;
+#endif
+ index = (int)node_index;
+
+ if (!yaml_parser_register_anchor(parser, index,
+ first_event->data.mapping_start.anchor)) return 0;
+
+ if (!yaml_parser_parse(parser, &event)) return 0;
+
+ while (event.type != YAML_MAPPING_END_EVENT) {
+ if (!STACK_LIMIT(parser,
+ parser->document->nodes.start[index-1].data.mapping.pairs,
+ INT_MAX-1)) return 0;
+ pair.key = yaml_parser_load_node(parser, &event);
+ if (!pair.key) return 0;
+ if (!yaml_parser_parse(parser, &event)) return 0;
+ pair.value = yaml_parser_load_node(parser, &event);
+ if (!pair.value) return 0;
+ if (!PUSH(parser,
+ parser->document->nodes.start[index-1].data.mapping.pairs,
+ pair)) return 0;
+ if (!yaml_parser_parse(parser, &event)) return 0;
+ }
+
+ parser->document->nodes.start[index-1].end_mark = event.end_mark;
+
+ return index;
+
+error:
+ yaml_free(tag);
+ yaml_free(first_event->data.mapping_start.anchor);
+ return 0;
+}
+
diff --git a/ext/psych/yaml/parser.c b/ext/psych/yaml/parser.c
new file mode 100644
index 0000000..32671b2
--- /dev/null
+++ b/ext/psych/yaml/parser.c
@@ -0,0 +1,1370 @@
+
+/*
+ * The parser implements the following grammar:
+ *
+ * stream ::= STREAM-START implicit_document? explicit_document* STREAM-END
+ * implicit_document ::= block_node DOCUMENT-END*
+ * explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
+ * block_node_or_indentless_sequence ::=
+ * ALIAS
+ * | properties (block_content | indentless_block_sequence)?
+ * | block_content
+ * | indentless_block_sequence
+ * block_node ::= ALIAS
+ * | properties block_content?
+ * | block_content
+ * flow_node ::= ALIAS
+ * | properties flow_content?
+ * | flow_content
+ * properties ::= TAG ANCHOR? | ANCHOR TAG?
+ * block_content ::= block_collection | flow_collection | SCALAR
+ * flow_content ::= flow_collection | SCALAR
+ * block_collection ::= block_sequence | block_mapping
+ * flow_collection ::= flow_sequence | flow_mapping
+ * block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END
+ * indentless_sequence ::= (BLOCK-ENTRY block_node?)+
+ * block_mapping ::= BLOCK-MAPPING_START
+ * ((KEY block_node_or_indentless_sequence?)?
+ * (VALUE block_node_or_indentless_sequence?)?)*
+ * BLOCK-END
+ * flow_sequence ::= FLOW-SEQUENCE-START
+ * (flow_sequence_entry FLOW-ENTRY)*
+ * flow_sequence_entry?
+ * FLOW-SEQUENCE-END
+ * flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
+ * flow_mapping ::= FLOW-MAPPING-START
+ * (flow_mapping_entry FLOW-ENTRY)*
+ * flow_mapping_entry?
+ * FLOW-MAPPING-END
+ * flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
+ */
+
+#include "yaml_private.h"
+
+/*
+ * Peek the next token in the token queue.
+ */
+
+#define PEEK_TOKEN(parser) \
+ ((parser->token_available || yaml_parser_fetch_more_tokens(parser)) ? \
+ parser->tokens.head : NULL)
+
+/*
+ * Remove the next token from the queue (must be called after PEEK_TOKEN).
+ */
+
+#define SKIP_TOKEN(parser) \
+ (parser->token_available = 0, \
+ parser->tokens_parsed ++, \
+ parser->stream_end_produced = \
+ (parser->tokens.head->type == YAML_STREAM_END_TOKEN), \
+ parser->tokens.head ++)
+
+/*
+ * Public API declarations.
+ */
+
+YAML_DECLARE(int)
+yaml_parser_parse(yaml_parser_t *parser, yaml_event_t *event);
+
+/*
+ * Error handling.
+ */
+
+static int
+yaml_parser_set_parser_error(yaml_parser_t *parser,
+ const char *problem, yaml_mark_t problem_mark);
+
+static int
+yaml_parser_set_parser_error_context(yaml_parser_t *parser,
+ const char *context, yaml_mark_t context_mark,
+ const char *problem, yaml_mark_t problem_mark);
+
+/*
+ * State functions.
+ */
+
+static int
+yaml_parser_state_machine(yaml_parser_t *parser, yaml_event_t *event);
+
+static int
+yaml_parser_parse_stream_start(yaml_parser_t *parser, yaml_event_t *event);
+
+static int
+yaml_parser_parse_document_start(yaml_parser_t *parser, yaml_event_t *event,
+ int implicit);
+
+static int
+yaml_parser_parse_document_content(yaml_parser_t *parser, yaml_event_t *event);
+
+static int
+yaml_parser_parse_document_end(yaml_parser_t *parser, yaml_event_t *event);
+
+static int
+yaml_parser_parse_node(yaml_parser_t *parser, yaml_event_t *event,
+ int block, int indentless_sequence);
+
+static int
+yaml_parser_parse_block_sequence_entry(yaml_parser_t *parser,
+ yaml_event_t *event, int first);
+
+static int
+yaml_parser_parse_indentless_sequence_entry(yaml_parser_t *parser,
+ yaml_event_t *event);
+
+static int
+yaml_parser_parse_block_mapping_key(yaml_parser_t *parser,
+ yaml_event_t *event, int first);
+
+static int
+yaml_parser_parse_block_mapping_value(yaml_parser_t *parser,
+ yaml_event_t *event);
+
+static int
+yaml_parser_parse_flow_sequence_entry(yaml_parser_t *parser,
+ yaml_event_t *event, int first);
+
+static int
+yaml_parser_parse_flow_sequence_entry_mapping_key(yaml_parser_t *parser,
+ yaml_event_t *event);
+
+static int
+yaml_parser_parse_flow_sequence_entry_mapping_value(yaml_parser_t *parser,
+ yaml_event_t *event);
+
+static int
+yaml_parser_parse_flow_sequence_entry_mapping_end(yaml_parser_t *parser,
+ yaml_event_t *event);
+
+static int
+yaml_parser_parse_flow_mapping_key(yaml_parser_t *parser,
+ yaml_event_t *event, int first);
+
+static int
+yaml_parser_parse_flow_mapping_value(yaml_parser_t *parser,
+ yaml_event_t *event, int empty);
+
+/*
+ * Utility functions.
+ */
+
+static int
+yaml_parser_process_empty_scalar(yaml_parser_t *parser,
+ yaml_event_t *event, yaml_mark_t mark);
+
+static int
+yaml_parser_process_directives(yaml_parser_t *parser,
+ yaml_version_directive_t **version_directive_ref,
+ yaml_tag_directive_t **tag_directives_start_ref,
+ yaml_tag_directive_t **tag_directives_end_ref);
+
+static int
+yaml_parser_append_tag_directive(yaml_parser_t *parser,
+ yaml_tag_directive_t value, int allow_duplicates, yaml_mark_t mark);
+
+/*
+ * Get the next event.
+ */
+
+YAML_DECLARE(int)
+yaml_parser_parse(yaml_parser_t *parser, yaml_event_t *event)
+{
+ assert(parser); /* Non-NULL parser object is expected. */
+ assert(event); /* Non-NULL event object is expected. */
+
+ /* Erase the event object. */
+
+ memset(event, 0, sizeof(yaml_event_t));
+
+ /* No events after the end of the stream or error. */
+
+ if (parser->stream_end_produced || parser->error ||
+ parser->state == YAML_PARSE_END_STATE) {
+ return 1;
+ }
+
+ /* Generate the next event. */
+
+ return yaml_parser_state_machine(parser, event);
+}
+
+/*
+ * Set parser error.
+ */
+
+static int
+yaml_parser_set_parser_error(yaml_parser_t *parser,
+ const char *problem, yaml_mark_t problem_mark)
+{
+ parser->error = YAML_PARSER_ERROR;
+ parser->problem = problem;
+ parser->problem_mark = problem_mark;
+
+ return 0;
+}
+
+static int
+yaml_parser_set_parser_error_context(yaml_parser_t *parser,
+ const char *context, yaml_mark_t context_mark,
+ const char *problem, yaml_mark_t problem_mark)
+{
+ parser->error = YAML_PARSER_ERROR;
+ parser->context = context;
+ parser->context_mark = context_mark;
+ parser->problem = problem;
+ parser->problem_mark = problem_mark;
+
+ return 0;
+}
+
+
+/*
+ * State dispatcher.
+ */
+
+static int
+yaml_parser_state_machine(yaml_parser_t *parser, yaml_event_t *event)
+{
+ switch (parser->state)
+ {
+ case YAML_PARSE_STREAM_START_STATE:
+ return yaml_parser_parse_stream_start(parser, event);
+
+ case YAML_PARSE_IMPLICIT_DOCUMENT_START_STATE:
+ return yaml_parser_parse_document_start(parser, event, 1);
+
+ case YAML_PARSE_DOCUMENT_START_STATE:
+ return yaml_parser_parse_document_start(parser, event, 0);
+
+ case YAML_PARSE_DOCUMENT_CONTENT_STATE:
+ return yaml_parser_parse_document_content(parser, event);
+
+ case YAML_PARSE_DOCUMENT_END_STATE:
+ return yaml_parser_parse_document_end(parser, event);
+
+ case YAML_PARSE_BLOCK_NODE_STATE:
+ return yaml_parser_parse_node(parser, event, 1, 0);
+
+ case YAML_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE:
+ return yaml_parser_parse_node(parser, event, 1, 1);
+
+ case YAML_PARSE_FLOW_NODE_STATE:
+ return yaml_parser_parse_node(parser, event, 0, 0);
+
+ case YAML_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE:
+ return yaml_parser_parse_block_sequence_entry(parser, event, 1);
+
+ case YAML_PARSE_BLOCK_SEQUENCE_ENTRY_STATE:
+ return yaml_parser_parse_block_sequence_entry(parser, event, 0);
+
+ case YAML_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE:
+ return yaml_parser_parse_indentless_sequence_entry(parser, event);
+
+ case YAML_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE:
+ return yaml_parser_parse_block_mapping_key(parser, event, 1);
+
+ case YAML_PARSE_BLOCK_MAPPING_KEY_STATE:
+ return yaml_parser_parse_block_mapping_key(parser, event, 0);
+
+ case YAML_PARSE_BLOCK_MAPPING_VALUE_STATE:
+ return yaml_parser_parse_block_mapping_value(parser, event);
+
+ case YAML_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE:
+ return yaml_parser_parse_flow_sequence_entry(parser, event, 1);
+
+ case YAML_PARSE_FLOW_SEQUENCE_ENTRY_STATE:
+ return yaml_parser_parse_flow_sequence_entry(parser, event, 0);
+
+ case YAML_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE:
+ return yaml_parser_parse_flow_sequence_entry_mapping_key(parser, event);
+
+ case YAML_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE:
+ return yaml_parser_parse_flow_sequence_entry_mapping_value(parser, event);
+
+ case YAML_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE:
+ return yaml_parser_parse_flow_sequence_entry_mapping_end(parser, event);
+
+ case YAML_PARSE_FLOW_MAPPING_FIRST_KEY_STATE:
+ return yaml_parser_parse_flow_mapping_key(parser, event, 1);
+
+ case YAML_PARSE_FLOW_MAPPING_KEY_STATE:
+ return yaml_parser_parse_flow_mapping_key(parser, event, 0);
+
+ case YAML_PARSE_FLOW_MAPPING_VALUE_STATE:
+ return yaml_parser_parse_flow_mapping_value(parser, event, 0);
+
+ case YAML_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE:
+ return yaml_parser_parse_flow_mapping_value(parser, event, 1);
+
+ default:
+ assert(1); /* Invalid state. */
+ }
+
+ return 0;
+}
+
+/*
+ * Parse the production:
+ * stream ::= STREAM-START implicit_document? explicit_document* STREAM-END
+ * ************
+ */
+
+static int
+yaml_parser_parse_stream_start(yaml_parser_t *parser, yaml_event_t *event)
+{
+ yaml_token_t *token;
+
+ token = PEEK_TOKEN(parser);
+ if (!token) return 0;
+
+ if (token->type != YAML_STREAM_START_TOKEN) {
+ return yaml_parser_set_parser_error(parser,
+ "did not find expected <stream-start>", token->start_mark);
+ }
+
+ parser->state = YAML_PARSE_IMPLICIT_DOCUMENT_START_STATE;
+ STREAM_START_EVENT_INIT(*event, token->data.stream_start.encoding,
+ token->start_mark, token->start_mark);
+ SKIP_TOKEN(parser);
+
+ return 1;
+}
+
+/*
+ * Parse the productions:
+ * implicit_document ::= block_node DOCUMENT-END*
+ * *
+ * explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
+ * *************************
+ */
+
+static int
+yaml_parser_parse_document_start(yaml_parser_t *parser, yaml_event_t *event,
+ int implicit)
+{
+ yaml_token_t *token;
+ yaml_version_directive_t *version_directive = NULL;
+ struct {
+ yaml_tag_directive_t *start;
+ yaml_tag_directive_t *end;
+ } tag_directives = { NULL, NULL };
+
+ token = PEEK_TOKEN(parser);
+ if (!token) return 0;
+
+ /* Parse extra document end indicators. */
+
+ if (!implicit)
+ {
+ while (token->type == YAML_DOCUMENT_END_TOKEN) {
+ SKIP_TOKEN(parser);
+ token = PEEK_TOKEN(parser);
+ if (!token) return 0;
+ }
+ }
+
+ /* Parse an implicit document. */
+
+ if (implicit && token->type != YAML_VERSION_DIRECTIVE_TOKEN &&
+ token->type != YAML_TAG_DIRECTIVE_TOKEN &&
+ token->type != YAML_DOCUMENT_START_TOKEN &&
+ token->type != YAML_STREAM_END_TOKEN)
+ {
+ if (!yaml_parser_process_directives(parser, NULL, NULL, NULL))
+ return 0;
+ if (!PUSH(parser, parser->states, YAML_PARSE_DOCUMENT_END_STATE))
+ return 0;
+ parser->state = YAML_PARSE_BLOCK_NODE_STATE;
+ DOCUMENT_START_EVENT_INIT(*event, NULL, NULL, NULL, 1,
+ token->start_mark, token->start_mark);
+ return 1;
+ }
+
+ /* Parse an explicit document. */
+
+ else if (token->type != YAML_STREAM_END_TOKEN)
+ {
+ yaml_mark_t start_mark, end_mark;
+ start_mark = token->start_mark;
+ if (!yaml_parser_process_directives(parser, &version_directive,
+ &tag_directives.start, &tag_directives.end))
+ return 0;
+ token = PEEK_TOKEN(parser);
+ if (!token) goto error;
+ if (token->type != YAML_DOCUMENT_START_TOKEN) {
+ yaml_parser_set_parser_error(parser,
+ "did not find expected <document start>", token->start_mark);
+ goto error;
+ }
+ if (!PUSH(parser, parser->states, YAML_PARSE_DOCUMENT_END_STATE))
+ goto error;
+ parser->state = YAML_PARSE_DOCUMENT_CONTENT_STATE;
+ end_mark = token->end_mark;
+ DOCUMENT_START_EVENT_INIT(*event, version_directive,
+ tag_directives.start, tag_directives.end, 0,
+ start_mark, end_mark);
+ SKIP_TOKEN(parser);
+ version_directive = NULL;
+ tag_directives.start = tag_directives.end = NULL;
+ return 1;
+ }
+
+ /* Parse the stream end. */
+
+ else
+ {
+ parser->state = YAML_PARSE_END_STATE;
+ STREAM_END_EVENT_INIT(*event, token->start_mark, token->end_mark);
+ SKIP_TOKEN(parser);
+ return 1;
+ }
+
+error:
+ yaml_free(version_directive);
+ while (tag_directives.start != tag_directives.end) {
+ yaml_free(tag_directives.end[-1].handle);
+ yaml_free(tag_directives.end[-1].prefix);
+ tag_directives.end --;
+ }
+ yaml_free(tag_directives.start);
+ return 0;
+}
+
+/*
+ * Parse the productions:
+ * explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
+ * ***********
+ */
+
+static int
+yaml_parser_parse_document_content(yaml_parser_t *parser, yaml_event_t *event)
+{
+ yaml_token_t *token;
+
+ token = PEEK_TOKEN(parser);
+ if (!token) return 0;
+
+ if (token->type == YAML_VERSION_DIRECTIVE_TOKEN ||
+ token->type == YAML_TAG_DIRECTIVE_TOKEN ||
+ token->type == YAML_DOCUMENT_START_TOKEN ||
+ token->type == YAML_DOCUMENT_END_TOKEN ||
+ token->type == YAML_STREAM_END_TOKEN) {
+ parser->state = POP(parser, parser->states);
+ return yaml_parser_process_empty_scalar(parser, event,
+ token->start_mark);
+ }
+ else {
+ return yaml_parser_parse_node(parser, event, 1, 0);
+ }
+}
+
+/*
+ * Parse the productions:
+ * implicit_document ::= block_node DOCUMENT-END*
+ * *************
+ * explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
+ * *************
+ */
+
+static int
+yaml_parser_parse_document_end(yaml_parser_t *parser, yaml_event_t *event)
+{
+ yaml_token_t *token;
+ yaml_mark_t start_mark, end_mark;
+ int implicit = 1;
+
+ token = PEEK_TOKEN(parser);
+ if (!token) return 0;
+
+ start_mark = end_mark = token->start_mark;
+
+ if (token->type == YAML_DOCUMENT_END_TOKEN) {
+ end_mark = token->end_mark;
+ SKIP_TOKEN(parser);
+ implicit = 0;
+ }
+
+ while (!STACK_EMPTY(parser, parser->tag_directives)) {
+ yaml_tag_directive_t tag_directive = POP(parser, parser->tag_directives);
+ yaml_free(tag_directive.handle);
+ yaml_free(tag_directive.prefix);
+ }
+
+ parser->state = YAML_PARSE_DOCUMENT_START_STATE;
+ DOCUMENT_END_EVENT_INIT(*event, implicit, start_mark, end_mark);
+
+ return 1;
+}
+
+/*
+ * Parse the productions:
+ * block_node_or_indentless_sequence ::=
+ * ALIAS
+ * *****
+ * | properties (block_content | indentless_block_sequence)?
+ * ********** *
+ * | block_content | indentless_block_sequence
+ * *
+ * block_node ::= ALIAS
+ * *****
+ * | properties block_content?
+ * ********** *
+ * | block_content
+ * *
+ * flow_node ::= ALIAS
+ * *****
+ * | properties flow_content?
+ * ********** *
+ * | flow_content
+ * *
+ * properties ::= TAG ANCHOR? | ANCHOR TAG?
+ * *************************
+ * block_content ::= block_collection | flow_collection | SCALAR
+ * ******
+ * flow_content ::= flow_collection | SCALAR
+ * ******
+ */
+
+static int
+yaml_parser_parse_node(yaml_parser_t *parser, yaml_event_t *event,
+ int block, int indentless_sequence)
+{
+ yaml_token_t *token;
+ yaml_char_t *anchor = NULL;
+ yaml_char_t *tag_handle = NULL;
+ yaml_char_t *tag_suffix = NULL;
+ yaml_char_t *tag = NULL;
+ yaml_mark_t start_mark, end_mark, tag_mark;
+ int implicit;
+
+ token = PEEK_TOKEN(parser);
+ if (!token) return 0;
+
+ if (token->type == YAML_ALIAS_TOKEN)
+ {
+ parser->state = POP(parser, parser->states);
+ ALIAS_EVENT_INIT(*event, token->data.alias.value,
+ token->start_mark, token->end_mark);
+ SKIP_TOKEN(parser);
+ return 1;
+ }
+
+ else
+ {
+ start_mark = end_mark = token->start_mark;
+
+ if (token->type == YAML_ANCHOR_TOKEN)
+ {
+ anchor = token->data.anchor.value;
+ start_mark = token->start_mark;
+ end_mark = token->end_mark;
+ SKIP_TOKEN(parser);
+ token = PEEK_TOKEN(parser);
+ if (!token) goto error;
+ if (token->type == YAML_TAG_TOKEN)
+ {
+ tag_handle = token->data.tag.handle;
+ tag_suffix = token->data.tag.suffix;
+ tag_mark = token->start_mark;
+ end_mark = token->end_mark;
+ SKIP_TOKEN(parser);
+ token = PEEK_TOKEN(parser);
+ if (!token) goto error;
+ }
+ }
+ else if (token->type == YAML_TAG_TOKEN)
+ {
+ tag_handle = token->data.tag.handle;
+ tag_suffix = token->data.tag.suffix;
+ start_mark = tag_mark = token->start_mark;
+ end_mark = token->end_mark;
+ SKIP_TOKEN(parser);
+ token = PEEK_TOKEN(parser);
+ if (!token) goto error;
+ if (token->type == YAML_ANCHOR_TOKEN)
+ {
+ anchor = token->data.anchor.value;
+ end_mark = token->end_mark;
+ SKIP_TOKEN(parser);
+ token = PEEK_TOKEN(parser);
+ if (!token) goto error;
+ }
+ }
+
+ if (tag_handle) {
+ if (!*tag_handle) {
+ tag = tag_suffix;
+ yaml_free(tag_handle);
+ tag_handle = tag_suffix = NULL;
+ }
+ else {
+ yaml_tag_directive_t *tag_directive;
+ for (tag_directive = parser->tag_directives.start;
+ tag_directive != parser->tag_directives.top;
+ tag_directive ++) {
+ if (strcmp((char *)tag_directive->handle, (char *)tag_handle) == 0) {
+ size_t prefix_len = strlen((char *)tag_directive->prefix);
+ size_t suffix_len = strlen((char *)tag_suffix);
+ tag = yaml_malloc(prefix_len+suffix_len+1);
+ if (!tag) {
+ parser->error = YAML_MEMORY_ERROR;
+ goto error;
+ }
+ memcpy(tag, tag_directive->prefix, prefix_len);
+ memcpy(tag+prefix_len, tag_suffix, suffix_len);
+ tag[prefix_len+suffix_len] = '\0';
+ yaml_free(tag_handle);
+ yaml_free(tag_suffix);
+ tag_handle = tag_suffix = NULL;
+ break;
+ }
+ }
+ if (!tag) {
+ yaml_parser_set_parser_error_context(parser,
+ "while parsing a node", start_mark,
+ "found undefined tag handle", tag_mark);
+ goto error;
+ }
+ }
+ }
+
+ implicit = (!tag || !*tag);
+ if (indentless_sequence && token->type == YAML_BLOCK_ENTRY_TOKEN) {
+ end_mark = token->end_mark;
+ parser->state = YAML_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE;
+ SEQUENCE_START_EVENT_INIT(*event, anchor, tag, implicit,
+ YAML_BLOCK_SEQUENCE_STYLE, start_mark, end_mark);
+ return 1;
+ }
+ else {
+ if (token->type == YAML_SCALAR_TOKEN) {
+ int plain_implicit = 0;
+ int quoted_implicit = 0;
+ end_mark = token->end_mark;
+ if ((token->data.scalar.style == YAML_PLAIN_SCALAR_STYLE && !tag)
+ || (tag && strcmp((char *)tag, "!") == 0)) {
+ plain_implicit = 1;
+ }
+ else if (!tag) {
+ quoted_implicit = 1;
+ }
+ parser->state = POP(parser, parser->states);
+ SCALAR_EVENT_INIT(*event, anchor, tag,
+ token->data.scalar.value, token->data.scalar.length,
+ plain_implicit, quoted_implicit,
+ token->data.scalar.style, start_mark, end_mark);
+ SKIP_TOKEN(parser);
+ return 1;
+ }
+ else if (token->type == YAML_FLOW_SEQUENCE_START_TOKEN) {
+ end_mark = token->end_mark;
+ parser->state = YAML_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE;
+ SEQUENCE_START_EVENT_INIT(*event, anchor, tag, implicit,
+ YAML_FLOW_SEQUENCE_STYLE, start_mark, end_mark);
+ return 1;
+ }
+ else if (token->type == YAML_FLOW_MAPPING_START_TOKEN) {
+ end_mark = token->end_mark;
+ parser->state = YAML_PARSE_FLOW_MAPPING_FIRST_KEY_STATE;
+ MAPPING_START_EVENT_INIT(*event, anchor, tag, implicit,
+ YAML_FLOW_MAPPING_STYLE, start_mark, end_mark);
+ return 1;
+ }
+ else if (block && token->type == YAML_BLOCK_SEQUENCE_START_TOKEN) {
+ end_mark = token->end_mark;
+ parser->state = YAML_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE;
+ SEQUENCE_START_EVENT_INIT(*event, anchor, tag, implicit,
+ YAML_BLOCK_SEQUENCE_STYLE, start_mark, end_mark);
+ return 1;
+ }
+ else if (block && token->type == YAML_BLOCK_MAPPING_START_TOKEN) {
+ end_mark = token->end_mark;
+ parser->state = YAML_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE;
+ MAPPING_START_EVENT_INIT(*event, anchor, tag, implicit,
+ YAML_BLOCK_MAPPING_STYLE, start_mark, end_mark);
+ return 1;
+ }
+ else if (anchor || tag) {
+ yaml_char_t *value = yaml_malloc(1);
+ if (!value) {
+ parser->error = YAML_MEMORY_ERROR;
+ goto error;
+ }
+ value[0] = '\0';
+ parser->state = POP(parser, parser->states);
+ SCALAR_EVENT_INIT(*event, anchor, tag, value, 0,
+ implicit, 0, YAML_PLAIN_SCALAR_STYLE,
+ start_mark, end_mark);
+ return 1;
+ }
+ else {
+ yaml_parser_set_parser_error_context(parser,
+ (block ? "while parsing a block node"
+ : "while parsing a flow node"), start_mark,
+ "did not find expected node content", token->start_mark);
+ goto error;
+ }
+ }
+ }
+
+error:
+ yaml_free(anchor);
+ yaml_free(tag_handle);
+ yaml_free(tag_suffix);
+ yaml_free(tag);
+
+ return 0;
+}
+
+/*
+ * Parse the productions:
+ * block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END
+ * ******************** *********** * *********
+ */
+
+static int
+yaml_parser_parse_block_sequence_entry(yaml_parser_t *parser,
+ yaml_event_t *event, int first)
+{
+ yaml_token_t *token;
+
+ if (first) {
+ token = PEEK_TOKEN(parser);
+ if (!PUSH(parser, parser->marks, token->start_mark))
+ return 0;
+ SKIP_TOKEN(parser);
+ }
+
+ token = PEEK_TOKEN(parser);
+ if (!token) return 0;
+
+ if (token->type == YAML_BLOCK_ENTRY_TOKEN)
+ {
+ yaml_mark_t mark = token->end_mark;
+ SKIP_TOKEN(parser);
+ token = PEEK_TOKEN(parser);
+ if (!token) return 0;
+ if (token->type != YAML_BLOCK_ENTRY_TOKEN &&
+ token->type != YAML_BLOCK_END_TOKEN) {
+ if (!PUSH(parser, parser->states,
+ YAML_PARSE_BLOCK_SEQUENCE_ENTRY_STATE))
+ return 0;
+ return yaml_parser_parse_node(parser, event, 1, 0);
+ }
+ else {
+ parser->state = YAML_PARSE_BLOCK_SEQUENCE_ENTRY_STATE;
+ return yaml_parser_process_empty_scalar(parser, event, mark);
+ }
+ }
+
+ else if (token->type == YAML_BLOCK_END_TOKEN)
+ {
+ parser->state = POP(parser, parser->states);
+ (void)POP(parser, parser->marks);
+ SEQUENCE_END_EVENT_INIT(*event, token->start_mark, token->end_mark);
+ SKIP_TOKEN(parser);
+ return 1;
+ }
+
+ else
+ {
+ return yaml_parser_set_parser_error_context(parser,
+ "while parsing a block collection", POP(parser, parser->marks),
+ "did not find expected '-' indicator", token->start_mark);
+ }
+}
+
+/*
+ * Parse the productions:
+ * indentless_sequence ::= (BLOCK-ENTRY block_node?)+
+ * *********** *
+ */
+
+static int
+yaml_parser_parse_indentless_sequence_entry(yaml_parser_t *parser,
+ yaml_event_t *event)
+{
+ yaml_token_t *token;
+
+ token = PEEK_TOKEN(parser);
+ if (!token) return 0;
+
+ if (token->type == YAML_BLOCK_ENTRY_TOKEN)
+ {
+ yaml_mark_t mark = token->end_mark;
+ SKIP_TOKEN(parser);
+ token = PEEK_TOKEN(parser);
+ if (!token) return 0;
+ if (token->type != YAML_BLOCK_ENTRY_TOKEN &&
+ token->type != YAML_KEY_TOKEN &&
+ token->type != YAML_VALUE_TOKEN &&
+ token->type != YAML_BLOCK_END_TOKEN) {
+ if (!PUSH(parser, parser->states,
+ YAML_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE))
+ return 0;
+ return yaml_parser_parse_node(parser, event, 1, 0);
+ }
+ else {
+ parser->state = YAML_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE;
+ return yaml_parser_process_empty_scalar(parser, event, mark);
+ }
+ }
+
+ else
+ {
+ parser->state = POP(parser, parser->states);
+ SEQUENCE_END_EVENT_INIT(*event, token->start_mark, token->start_mark);
+ return 1;
+ }
+}
+
+/*
+ * Parse the productions:
+ * block_mapping ::= BLOCK-MAPPING_START
+ * *******************
+ * ((KEY block_node_or_indentless_sequence?)?
+ * *** *
+ * (VALUE block_node_or_indentless_sequence?)?)*
+ *
+ * BLOCK-END
+ * *********
+ */
+
+static int
+yaml_parser_parse_block_mapping_key(yaml_parser_t *parser,
+ yaml_event_t *event, int first)
+{
+ yaml_token_t *token;
+
+ if (first) {
+ token = PEEK_TOKEN(parser);
+ if (!PUSH(parser, parser->marks, token->start_mark))
+ return 0;
+ SKIP_TOKEN(parser);
+ }
+
+ token = PEEK_TOKEN(parser);
+ if (!token) return 0;
+
+ if (token->type == YAML_KEY_TOKEN)
+ {
+ yaml_mark_t mark = token->end_mark;
+ SKIP_TOKEN(parser);
+ token = PEEK_TOKEN(parser);
+ if (!token) return 0;
+ if (token->type != YAML_KEY_TOKEN &&
+ token->type != YAML_VALUE_TOKEN &&
+ token->type != YAML_BLOCK_END_TOKEN) {
+ if (!PUSH(parser, parser->states,
+ YAML_PARSE_BLOCK_MAPPING_VALUE_STATE))
+ return 0;
+ return yaml_parser_parse_node(parser, event, 1, 1);
+ }
+ else {
+ parser->state = YAML_PARSE_BLOCK_MAPPING_VALUE_STATE;
+ return yaml_parser_process_empty_scalar(parser, event, mark);
+ }
+ }
+
+ else if (token->type == YAML_BLOCK_END_TOKEN)
+ {
+ parser->state = POP(parser, parser->states);
+ (void)POP(parser, parser->marks);
+ MAPPING_END_EVENT_INIT(*event, token->start_mark, token->end_mark);
+ SKIP_TOKEN(parser);
+ return 1;
+ }
+
+ else
+ {
+ return yaml_parser_set_parser_error_context(parser,
+ "while parsing a block mapping", POP(parser, parser->marks),
+ "did not find expected key", token->start_mark);
+ }
+}
+
+/*
+ * Parse the productions:
+ * block_mapping ::= BLOCK-MAPPING_START
+ *
+ * ((KEY block_node_or_indentless_sequence?)?
+ *
+ * (VALUE block_node_or_indentless_sequence?)?)*
+ * ***** *
+ * BLOCK-END
+ *
+ */
+
+static int
+yaml_parser_parse_block_mapping_value(yaml_parser_t *parser,
+ yaml_event_t *event)
+{
+ yaml_token_t *token;
+
+ token = PEEK_TOKEN(parser);
+ if (!token) return 0;
+
+ if (token->type == YAML_VALUE_TOKEN)
+ {
+ yaml_mark_t mark = token->end_mark;
+ SKIP_TOKEN(parser);
+ token = PEEK_TOKEN(parser);
+ if (!token) return 0;
+ if (token->type != YAML_KEY_TOKEN &&
+ token->type != YAML_VALUE_TOKEN &&
+ token->type != YAML_BLOCK_END_TOKEN) {
+ if (!PUSH(parser, parser->states,
+ YAML_PARSE_BLOCK_MAPPING_KEY_STATE))
+ return 0;
+ return yaml_parser_parse_node(parser, event, 1, 1);
+ }
+ else {
+ parser->state = YAML_PARSE_BLOCK_MAPPING_KEY_STATE;
+ return yaml_parser_process_empty_scalar(parser, event, mark);
+ }
+ }
+
+ else
+ {
+ parser->state = YAML_PARSE_BLOCK_MAPPING_KEY_STATE;
+ return yaml_parser_process_empty_scalar(parser, event, token->start_mark);
+ }
+}
+
+/*
+ * Parse the productions:
+ * flow_sequence ::= FLOW-SEQUENCE-START
+ * *******************
+ * (flow_sequence_entry FLOW-ENTRY)*
+ * * **********
+ * flow_sequence_entry?
+ * *
+ * FLOW-SEQUENCE-END
+ * *****************
+ * flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
+ * *
+ */
+
+static int
+yaml_parser_parse_flow_sequence_entry(yaml_parser_t *parser,
+ yaml_event_t *event, int first)
+{
+ yaml_token_t *token;
+
+ if (first) {
+ token = PEEK_TOKEN(parser);
+ if (!PUSH(parser, parser->marks, token->start_mark))
+ return 0;
+ SKIP_TOKEN(parser);
+ }
+
+ token = PEEK_TOKEN(parser);
+ if (!token) return 0;
+
+ if (token->type != YAML_FLOW_SEQUENCE_END_TOKEN)
+ {
+ if (!first) {
+ if (token->type == YAML_FLOW_ENTRY_TOKEN) {
+ SKIP_TOKEN(parser);
+ token = PEEK_TOKEN(parser);
+ if (!token) return 0;
+ }
+ else {
+ return yaml_parser_set_parser_error_context(parser,
+ "while parsing a flow sequence", POP(parser, parser->marks),
+ "did not find expected ',' or ']'", token->start_mark);
+ }
+ }
+
+ if (token->type == YAML_KEY_TOKEN) {
+ parser->state = YAML_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE;
+ MAPPING_START_EVENT_INIT(*event, NULL, NULL,
+ 1, YAML_FLOW_MAPPING_STYLE,
+ token->start_mark, token->end_mark);
+ SKIP_TOKEN(parser);
+ return 1;
+ }
+
+ else if (token->type != YAML_FLOW_SEQUENCE_END_TOKEN) {
+ if (!PUSH(parser, parser->states,
+ YAML_PARSE_FLOW_SEQUENCE_ENTRY_STATE))
+ return 0;
+ return yaml_parser_parse_node(parser, event, 0, 0);
+ }
+ }
+
+ parser->state = POP(parser, parser->states);
+ (void)POP(parser, parser->marks);
+ SEQUENCE_END_EVENT_INIT(*event, token->start_mark, token->end_mark);
+ SKIP_TOKEN(parser);
+ return 1;
+}
+
+/*
+ * Parse the productions:
+ * flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
+ * *** *
+ */
+
+static int
+yaml_parser_parse_flow_sequence_entry_mapping_key(yaml_parser_t *parser,
+ yaml_event_t *event)
+{
+ yaml_token_t *token;
+
+ token = PEEK_TOKEN(parser);
+ if (!token) return 0;
+
+ if (token->type != YAML_VALUE_TOKEN && token->type != YAML_FLOW_ENTRY_TOKEN
+ && token->type != YAML_FLOW_SEQUENCE_END_TOKEN) {
+ if (!PUSH(parser, parser->states,
+ YAML_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE))
+ return 0;
+ return yaml_parser_parse_node(parser, event, 0, 0);
+ }
+ else {
+ yaml_mark_t mark = token->end_mark;
+ SKIP_TOKEN(parser);
+ parser->state = YAML_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE;
+ return yaml_parser_process_empty_scalar(parser, event, mark);
+ }
+}
+
+/*
+ * Parse the productions:
+ * flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
+ * ***** *
+ */
+
+static int
+yaml_parser_parse_flow_sequence_entry_mapping_value(yaml_parser_t *parser,
+ yaml_event_t *event)
+{
+ yaml_token_t *token;
+
+ token = PEEK_TOKEN(parser);
+ if (!token) return 0;
+
+ if (token->type == YAML_VALUE_TOKEN) {
+ SKIP_TOKEN(parser);
+ token = PEEK_TOKEN(parser);
+ if (!token) return 0;
+ if (token->type != YAML_FLOW_ENTRY_TOKEN
+ && token->type != YAML_FLOW_SEQUENCE_END_TOKEN) {
+ if (!PUSH(parser, parser->states,
+ YAML_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE))
+ return 0;
+ return yaml_parser_parse_node(parser, event, 0, 0);
+ }
+ }
+ parser->state = YAML_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE;
+ return yaml_parser_process_empty_scalar(parser, event, token->start_mark);
+}
+
+/*
+ * Parse the productions:
+ * flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
+ * *
+ */
+
+static int
+yaml_parser_parse_flow_sequence_entry_mapping_end(yaml_parser_t *parser,
+ yaml_event_t *event)
+{
+ yaml_token_t *token;
+
+ token = PEEK_TOKEN(parser);
+ if (!token) return 0;
+
+ parser->state = YAML_PARSE_FLOW_SEQUENCE_ENTRY_STATE;
+
+ MAPPING_END_EVENT_INIT(*event, token->start_mark, token->start_mark);
+ return 1;
+}
+
+/*
+ * Parse the productions:
+ * flow_mapping ::= FLOW-MAPPING-START
+ * ******************
+ * (flow_mapping_entry FLOW-ENTRY)*
+ * * **********
+ * flow_mapping_entry?
+ * ******************
+ * FLOW-MAPPING-END
+ * ****************
+ * flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
+ * * *** *
+ */
+
+static int
+yaml_parser_parse_flow_mapping_key(yaml_parser_t *parser,
+ yaml_event_t *event, int first)
+{
+ yaml_token_t *token;
+
+ if (first) {
+ token = PEEK_TOKEN(parser);
+ if (!PUSH(parser, parser->marks, token->start_mark))
+ return 0;
+ SKIP_TOKEN(parser);
+ }
+
+ token = PEEK_TOKEN(parser);
+ if (!token) return 0;
+
+ if (token->type != YAML_FLOW_MAPPING_END_TOKEN)
+ {
+ if (!first) {
+ if (token->type == YAML_FLOW_ENTRY_TOKEN) {
+ SKIP_TOKEN(parser);
+ token = PEEK_TOKEN(parser);
+ if (!token) return 0;
+ }
+ else {
+ return yaml_parser_set_parser_error_context(parser,
+ "while parsing a flow mapping", POP(parser, parser->marks),
+ "did not find expected ',' or '}'", token->start_mark);
+ }
+ }
+
+ if (token->type == YAML_KEY_TOKEN) {
+ SKIP_TOKEN(parser);
+ token = PEEK_TOKEN(parser);
+ if (!token) return 0;
+ if (token->type != YAML_VALUE_TOKEN
+ && token->type != YAML_FLOW_ENTRY_TOKEN
+ && token->type != YAML_FLOW_MAPPING_END_TOKEN) {
+ if (!PUSH(parser, parser->states,
+ YAML_PARSE_FLOW_MAPPING_VALUE_STATE))
+ return 0;
+ return yaml_parser_parse_node(parser, event, 0, 0);
+ }
+ else {
+ parser->state = YAML_PARSE_FLOW_MAPPING_VALUE_STATE;
+ return yaml_parser_process_empty_scalar(parser, event,
+ token->start_mark);
+ }
+ }
+ else if (token->type != YAML_FLOW_MAPPING_END_TOKEN) {
+ if (!PUSH(parser, parser->states,
+ YAML_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE))
+ return 0;
+ return yaml_parser_parse_node(parser, event, 0, 0);
+ }
+ }
+
+ parser->state = POP(parser, parser->states);
+ (void)POP(parser, parser->marks);
+ MAPPING_END_EVENT_INIT(*event, token->start_mark, token->end_mark);
+ SKIP_TOKEN(parser);
+ return 1;
+}
+
+/*
+ * Parse the productions:
+ * flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
+ * * ***** *
+ */
+
+static int
+yaml_parser_parse_flow_mapping_value(yaml_parser_t *parser,
+ yaml_event_t *event, int empty)
+{
+ yaml_token_t *token;
+
+ token = PEEK_TOKEN(parser);
+ if (!token) return 0;
+
+ if (empty) {
+ parser->state = YAML_PARSE_FLOW_MAPPING_KEY_STATE;
+ return yaml_parser_process_empty_scalar(parser, event,
+ token->start_mark);
+ }
+
+ if (token->type == YAML_VALUE_TOKEN) {
+ SKIP_TOKEN(parser);
+ token = PEEK_TOKEN(parser);
+ if (!token) return 0;
+ if (token->type != YAML_FLOW_ENTRY_TOKEN
+ && token->type != YAML_FLOW_MAPPING_END_TOKEN) {
+ if (!PUSH(parser, parser->states,
+ YAML_PARSE_FLOW_MAPPING_KEY_STATE))
+ return 0;
+ return yaml_parser_parse_node(parser, event, 0, 0);
+ }
+ }
+
+ parser->state = YAML_PARSE_FLOW_MAPPING_KEY_STATE;
+ return yaml_parser_process_empty_scalar(parser, event, token->start_mark);
+}
+
+/*
+ * Generate an empty scalar event.
+ */
+
+static int
+yaml_parser_process_empty_scalar(yaml_parser_t *parser, yaml_event_t *event,
+ yaml_mark_t mark)
+{
+ yaml_char_t *value;
+
+ value = yaml_malloc(1);
+ if (!value) {
+ parser->error = YAML_MEMORY_ERROR;
+ return 0;
+ }
+ value[0] = '\0';
+
+ SCALAR_EVENT_INIT(*event, NULL, NULL, value, 0,
+ 1, 0, YAML_PLAIN_SCALAR_STYLE, mark, mark);
+
+ return 1;
+}
+
+/*
+ * Parse directives.
+ */
+
+static int
+yaml_parser_process_directives(yaml_parser_t *parser,
+ yaml_version_directive_t **version_directive_ref,
+ yaml_tag_directive_t **tag_directives_start_ref,
+ yaml_tag_directive_t **tag_directives_end_ref)
+{
+ yaml_tag_directive_t default_tag_directives[] = {
+ {(yaml_char_t *)"!", (yaml_char_t *)"!"},
+ {(yaml_char_t *)"!!", (yaml_char_t *)"tag:yaml.org,2002:"},
+ {NULL, NULL}
+ };
+ yaml_tag_directive_t *default_tag_directive;
+ yaml_version_directive_t *version_directive = NULL;
+ struct {
+ yaml_tag_directive_t *start;
+ yaml_tag_directive_t *end;
+ yaml_tag_directive_t *top;
+ } tag_directives = { NULL, NULL, NULL };
+ yaml_token_t *token;
+
+ if (!STACK_INIT(parser, tag_directives, INITIAL_STACK_SIZE))
+ goto error;
+
+ token = PEEK_TOKEN(parser);
+ if (!token) goto error;
+
+ while (token->type == YAML_VERSION_DIRECTIVE_TOKEN ||
+ token->type == YAML_TAG_DIRECTIVE_TOKEN)
+ {
+ if (token->type == YAML_VERSION_DIRECTIVE_TOKEN) {
+ if (version_directive) {
+ yaml_parser_set_parser_error(parser,
+ "found duplicate %YAML directive", token->start_mark);
+ goto error;
+ }
+ if (token->data.version_directive.major != 1
+ || token->data.version_directive.minor != 1) {
+ yaml_parser_set_parser_error(parser,
+ "found incompatible YAML document", token->start_mark);
+ goto error;
+ }
+ version_directive = yaml_malloc(sizeof(yaml_version_directive_t));
+ if (!version_directive) {
+ parser->error = YAML_MEMORY_ERROR;
+ goto error;
+ }
+ version_directive->major = token->data.version_directive.major;
+ version_directive->minor = token->data.version_directive.minor;
+ }
+
+ else if (token->type == YAML_TAG_DIRECTIVE_TOKEN) {
+ yaml_tag_directive_t value;
+ value.handle = token->data.tag_directive.handle;
+ value.prefix = token->data.tag_directive.prefix;
+
+ if (!yaml_parser_append_tag_directive(parser, value, 0,
+ token->start_mark))
+ goto error;
+ if (!PUSH(parser, tag_directives, value))
+ goto error;
+ }
+
+ SKIP_TOKEN(parser);
+ token = PEEK_TOKEN(parser);
+ if (!token) goto error;
+ }
+
+ for (default_tag_directive = default_tag_directives;
+ default_tag_directive->handle; default_tag_directive++) {
+ if (!yaml_parser_append_tag_directive(parser, *default_tag_directive, 1,
+ token->start_mark))
+ goto error;
+ }
+
+ if (version_directive_ref) {
+ *version_directive_ref = version_directive;
+ }
+ if (tag_directives_start_ref) {
+ if (STACK_EMPTY(parser, tag_directives)) {
+ *tag_directives_start_ref = *tag_directives_end_ref = NULL;
+ STACK_DEL(parser, tag_directives);
+ }
+ else {
+ *tag_directives_start_ref = tag_directives.start;
+ *tag_directives_end_ref = tag_directives.top;
+ }
+ }
+ else {
+ STACK_DEL(parser, tag_directives);
+ }
+
+ return 1;
+
+error:
+ yaml_free(version_directive);
+ while (!STACK_EMPTY(parser, tag_directives)) {
+ yaml_tag_directive_t tag_directive = POP(parser, tag_directives);
+ yaml_free(tag_directive.handle);
+ yaml_free(tag_directive.prefix);
+ }
+ STACK_DEL(parser, tag_directives);
+ return 0;
+}
+
+/*
+ * Append a tag directive to the directives stack.
+ */
+
+static int
+yaml_parser_append_tag_directive(yaml_parser_t *parser,
+ yaml_tag_directive_t value, int allow_duplicates, yaml_mark_t mark)
+{
+ yaml_tag_directive_t *tag_directive;
+ yaml_tag_directive_t copy = { NULL, NULL };
+
+ for (tag_directive = parser->tag_directives.start;
+ tag_directive != parser->tag_directives.top; tag_directive ++) {
+ if (strcmp((char *)value.handle, (char *)tag_directive->handle) == 0) {
+ if (allow_duplicates)
+ return 1;
+ return yaml_parser_set_parser_error(parser,
+ "found duplicate %TAG directive", mark);
+ }
+ }
+
+ copy.handle = yaml_strdup(value.handle);
+ copy.prefix = yaml_strdup(value.prefix);
+ if (!copy.handle || !copy.prefix) {
+ parser->error = YAML_MEMORY_ERROR;
+ goto error;
+ }
+
+ if (!PUSH(parser, parser->tag_directives, copy))
+ goto error;
+
+ return 1;
+
+error:
+ yaml_free(copy.handle);
+ yaml_free(copy.prefix);
+ return 0;
+}
+
diff --git a/ext/psych/yaml/reader.c b/ext/psych/yaml/reader.c
new file mode 100644
index 0000000..f1a06de
--- /dev/null
+++ b/ext/psych/yaml/reader.c
@@ -0,0 +1,469 @@
+
+#include "yaml_private.h"
+
+/*
+ * Declarations.
+ */
+
+static int
+yaml_parser_set_reader_error(yaml_parser_t *parser, const char *problem,
+ size_t offset, int value);
+
+static int
+yaml_parser_update_raw_buffer(yaml_parser_t *parser);
+
+static int
+yaml_parser_determine_encoding(yaml_parser_t *parser);
+
+YAML_DECLARE(int)
+yaml_parser_update_buffer(yaml_parser_t *parser, size_t length);
+
+/*
+ * Set the reader error and return 0.
+ */
+
+static int
+yaml_parser_set_reader_error(yaml_parser_t *parser, const char *problem,
+ size_t offset, int value)
+{
+ parser->error = YAML_READER_ERROR;
+ parser->problem = problem;
+ parser->problem_offset = offset;
+ parser->problem_value = value;
+
+ return 0;
+}
+
+/*
+ * Byte order marks.
+ */
+
+#define BOM_UTF8 "\xef\xbb\xbf"
+#define BOM_UTF16LE "\xff\xfe"
+#define BOM_UTF16BE "\xfe\xff"
+
+/*
+ * Determine the input stream encoding by checking the BOM symbol. If no BOM is
+ * found, the UTF-8 encoding is assumed. Return 1 on success, 0 on failure.
+ */
+
+static int
+yaml_parser_determine_encoding(yaml_parser_t *parser)
+{
+ /* Ensure that we had enough bytes in the raw buffer. */
+
+ while (!parser->eof
+ && parser->raw_buffer.last - parser->raw_buffer.pointer < 3) {
+ if (!yaml_parser_update_raw_buffer(parser)) {
+ return 0;
+ }
+ }
+
+ /* Determine the encoding. */
+
+ if (parser->raw_buffer.last - parser->raw_buffer.pointer >= 2
+ && !memcmp(parser->raw_buffer.pointer, BOM_UTF16LE, 2)) {
+ parser->encoding = YAML_UTF16LE_ENCODING;
+ parser->raw_buffer.pointer += 2;
+ parser->offset += 2;
+ }
+ else if (parser->raw_buffer.last - parser->raw_buffer.pointer >= 2
+ && !memcmp(parser->raw_buffer.pointer, BOM_UTF16BE, 2)) {
+ parser->encoding = YAML_UTF16BE_ENCODING;
+ parser->raw_buffer.pointer += 2;
+ parser->offset += 2;
+ }
+ else if (parser->raw_buffer.last - parser->raw_buffer.pointer >= 3
+ && !memcmp(parser->raw_buffer.pointer, BOM_UTF8, 3)) {
+ parser->encoding = YAML_UTF8_ENCODING;
+ parser->raw_buffer.pointer += 3;
+ parser->offset += 3;
+ }
+ else {
+ parser->encoding = YAML_UTF8_ENCODING;
+ }
+
+ return 1;
+}
+
+/*
+ * Update the raw buffer.
+ */
+
+static int
+yaml_parser_update_raw_buffer(yaml_parser_t *parser)
+{
+ size_t size_read = 0;
+
+ /* Return if the raw buffer is full. */
+
+ if (parser->raw_buffer.start == parser->raw_buffer.pointer
+ && parser->raw_buffer.last == parser->raw_buffer.end)
+ return 1;
+
+ /* Return on EOF. */
+
+ if (parser->eof) return 1;
+
+ /* Move the remaining bytes in the raw buffer to the beginning. */
+
+ if (parser->raw_buffer.start < parser->raw_buffer.pointer
+ && parser->raw_buffer.pointer < parser->raw_buffer.last) {
+ memmove(parser->raw_buffer.start, parser->raw_buffer.pointer,
+ parser->raw_buffer.last - parser->raw_buffer.pointer);
+ }
+ parser->raw_buffer.last -=
+ parser->raw_buffer.pointer - parser->raw_buffer.start;
+ parser->raw_buffer.pointer = parser->raw_buffer.start;
+
+ /* Call the read handler to fill the buffer. */
+
+ if (!parser->read_handler(parser->read_handler_data, parser->raw_buffer.last,
+ parser->raw_buffer.end - parser->raw_buffer.last, &size_read)) {
+ return yaml_parser_set_reader_error(parser, "input error",
+ parser->offset, -1);
+ }
+ parser->raw_buffer.last += size_read;
+ if (!size_read) {
+ parser->eof = 1;
+ }
+
+ return 1;
+}
+
+/*
+ * Ensure that the buffer contains at least `length` characters.
+ * Return 1 on success, 0 on failure.
+ *
+ * The length is supposed to be significantly less that the buffer size.
+ */
+
+YAML_DECLARE(int)
+yaml_parser_update_buffer(yaml_parser_t *parser, size_t length)
+{
+ int first = 1;
+
+ assert(parser->read_handler); /* Read handler must be set. */
+
+ /* If the EOF flag is set and the raw buffer is empty, do nothing. */
+
+ if (parser->eof && parser->raw_buffer.pointer == parser->raw_buffer.last)
+ return 1;
+
+ /* Return if the buffer contains enough characters. */
+
+ if (parser->unread >= length)
+ return 1;
+
+ /* Determine the input encoding if it is not known yet. */
+
+ if (!parser->encoding) {
+ if (!yaml_parser_determine_encoding(parser))
+ return 0;
+ }
+
+ /* Move the unread characters to the beginning of the buffer. */
+
+ if (parser->buffer.start < parser->buffer.pointer
+ && parser->buffer.pointer < parser->buffer.last) {
+ size_t size = parser->buffer.last - parser->buffer.pointer;
+ memmove(parser->buffer.start, parser->buffer.pointer, size);
+ parser->buffer.pointer = parser->buffer.start;
+ parser->buffer.last = parser->buffer.start + size;
+ }
+ else if (parser->buffer.pointer == parser->buffer.last) {
+ parser->buffer.pointer = parser->buffer.start;
+ parser->buffer.last = parser->buffer.start;
+ }
+
+ /* Fill the buffer until it has enough characters. */
+
+ while (parser->unread < length)
+ {
+ /* Fill the raw buffer if necessary. */
+
+ if (!first || parser->raw_buffer.pointer == parser->raw_buffer.last) {
+ if (!yaml_parser_update_raw_buffer(parser)) return 0;
+ }
+ first = 0;
+
+ /* Decode the raw buffer. */
+
+ while (parser->raw_buffer.pointer != parser->raw_buffer.last)
+ {
+ unsigned int value = 0, value2 = 0;
+ int incomplete = 0;
+ unsigned char octet;
+ unsigned int width = 0;
+ int low, high;
+ size_t k;
+ size_t raw_unread = parser->raw_buffer.last - parser->raw_buffer.pointer;
+
+ /* Decode the next character. */
+
+ switch (parser->encoding)
+ {
+ case YAML_UTF8_ENCODING:
+
+ /*
+ * Decode a UTF-8 character. Check RFC 3629
+ * (http://www.ietf.org/rfc/rfc3629.txt) for more details.
+ *
+ * The following table (taken from the RFC) is used for
+ * decoding.
+ *
+ * Char. number range | UTF-8 octet sequence
+ * (hexadecimal) | (binary)
+ * --------------------+------------------------------------
+ * 0000 0000-0000 007F | 0xxxxxxx
+ * 0000 0080-0000 07FF | 110xxxxx 10xxxxxx
+ * 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
+ * 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+ *
+ * Additionally, the characters in the range 0xD800-0xDFFF
+ * are prohibited as they are reserved for use with UTF-16
+ * surrogate pairs.
+ */
+
+ /* Determine the length of the UTF-8 sequence. */
+
+ octet = parser->raw_buffer.pointer[0];
+ width = (octet & 0x80) == 0x00 ? 1 :
+ (octet & 0xE0) == 0xC0 ? 2 :
+ (octet & 0xF0) == 0xE0 ? 3 :
+ (octet & 0xF8) == 0xF0 ? 4 : 0;
+
+ /* Check if the leading octet is valid. */
+
+ if (!width)
+ return yaml_parser_set_reader_error(parser,
+ "invalid leading UTF-8 octet",
+ parser->offset, octet);
+
+ /* Check if the raw buffer contains an incomplete character. */
+
+ if (width > raw_unread) {
+ if (parser->eof) {
+ return yaml_parser_set_reader_error(parser,
+ "incomplete UTF-8 octet sequence",
+ parser->offset, -1);
+ }
+ incomplete = 1;
+ break;
+ }
+
+ /* Decode the leading octet. */
+
+ value = (octet & 0x80) == 0x00 ? octet & 0x7F :
+ (octet & 0xE0) == 0xC0 ? octet & 0x1F :
+ (octet & 0xF0) == 0xE0 ? octet & 0x0F :
+ (octet & 0xF8) == 0xF0 ? octet & 0x07 : 0;
+
+ /* Check and decode the trailing octets. */
+
+ for (k = 1; k < width; k ++)
+ {
+ octet = parser->raw_buffer.pointer[k];
+
+ /* Check if the octet is valid. */
+
+ if ((octet & 0xC0) != 0x80)
+ return yaml_parser_set_reader_error(parser,
+ "invalid trailing UTF-8 octet",
+ parser->offset+k, octet);
+
+ /* Decode the octet. */
+
+ value = (value << 6) + (octet & 0x3F);
+ }
+
+ /* Check the length of the sequence against the value. */
+
+ if (!((width == 1) ||
+ (width == 2 && value >= 0x80) ||
+ (width == 3 && value >= 0x800) ||
+ (width == 4 && value >= 0x10000)))
+ return yaml_parser_set_reader_error(parser,
+ "invalid length of a UTF-8 sequence",
+ parser->offset, -1);
+
+ /* Check the range of the value. */
+
+ if ((value >= 0xD800 && value <= 0xDFFF) || value > 0x10FFFF)
+ return yaml_parser_set_reader_error(parser,
+ "invalid Unicode character",
+ parser->offset, value);
+
+ break;
+
+ case YAML_UTF16LE_ENCODING:
+ case YAML_UTF16BE_ENCODING:
+
+ low = (parser->encoding == YAML_UTF16LE_ENCODING ? 0 : 1);
+ high = (parser->encoding == YAML_UTF16LE_ENCODING ? 1 : 0);
+
+ /*
+ * The UTF-16 encoding is not as simple as one might
+ * naively think. Check RFC 2781
+ * (http://www.ietf.org/rfc/rfc2781.txt).
+ *
+ * Normally, two subsequent bytes describe a Unicode
+ * character. However a special technique (called a
+ * surrogate pair) is used for specifying character
+ * values larger than 0xFFFF.
+ *
+ * A surrogate pair consists of two pseudo-characters:
+ * high surrogate area (0xD800-0xDBFF)
+ * low surrogate area (0xDC00-0xDFFF)
+ *
+ * The following formulas are used for decoding
+ * and encoding characters using surrogate pairs:
+ *
+ * U = U' + 0x10000 (0x01 00 00 <= U <= 0x10 FF FF)
+ * U' = yyyyyyyyyyxxxxxxxxxx (0 <= U' <= 0x0F FF FF)
+ * W1 = 110110yyyyyyyyyy
+ * W2 = 110111xxxxxxxxxx
+ *
+ * where U is the character value, W1 is the high surrogate
+ * area, W2 is the low surrogate area.
+ */
+
+ /* Check for incomplete UTF-16 character. */
+
+ if (raw_unread < 2) {
+ if (parser->eof) {
+ return yaml_parser_set_reader_error(parser,
+ "incomplete UTF-16 character",
+ parser->offset, -1);
+ }
+ incomplete = 1;
+ break;
+ }
+
+ /* Get the character. */
+
+ value = parser->raw_buffer.pointer[low]
+ + (parser->raw_buffer.pointer[high] << 8);
+
+ /* Check for unexpected low surrogate area. */
+
+ if ((value & 0xFC00) == 0xDC00)
+ return yaml_parser_set_reader_error(parser,
+ "unexpected low surrogate area",
+ parser->offset, value);
+
+ /* Check for a high surrogate area. */
+
+ if ((value & 0xFC00) == 0xD800) {
+
+ width = 4;
+
+ /* Check for incomplete surrogate pair. */
+
+ if (raw_unread < 4) {
+ if (parser->eof) {
+ return yaml_parser_set_reader_error(parser,
+ "incomplete UTF-16 surrogate pair",
+ parser->offset, -1);
+ }
+ incomplete = 1;
+ break;
+ }
+
+ /* Get the next character. */
+
+ value2 = parser->raw_buffer.pointer[low+2]
+ + (parser->raw_buffer.pointer[high+2] << 8);
+
+ /* Check for a low surrogate area. */
+
+ if ((value2 & 0xFC00) != 0xDC00)
+ return yaml_parser_set_reader_error(parser,
+ "expected low surrogate area",
+ parser->offset+2, value2);
+
+ /* Generate the value of the surrogate pair. */
+
+ value = 0x10000 + ((value & 0x3FF) << 10) + (value2 & 0x3FF);
+ }
+
+ else {
+ width = 2;
+ }
+
+ break;
+
+ default:
+ assert(1); /* Impossible. */
+ }
+
+ /* Check if the raw buffer contains enough bytes to form a character. */
+
+ if (incomplete) break;
+
+ /*
+ * Check if the character is in the allowed range:
+ * #x9 | #xA | #xD | [#x20-#x7E] (8 bit)
+ * | #x85 | [#xA0-#xD7FF] | [#xE000-#xFFFD] (16 bit)
+ * | [#x10000-#x10FFFF] (32 bit)
+ */
+
+ if (! (value == 0x09 || value == 0x0A || value == 0x0D
+ || (value >= 0x20 && value <= 0x7E)
+ || (value == 0x85) || (value >= 0xA0 && value <= 0xD7FF)
+ || (value >= 0xE000 && value <= 0xFFFD)
+ || (value >= 0x10000 && value <= 0x10FFFF)))
+ return yaml_parser_set_reader_error(parser,
+ "control characters are not allowed",
+ parser->offset, value);
+
+ /* Move the raw pointers. */
+
+ parser->raw_buffer.pointer += width;
+ parser->offset += width;
+
+ /* Finally put the character into the buffer. */
+
+ /* 0000 0000-0000 007F -> 0xxxxxxx */
+ if (value <= 0x7F) {
+ *(parser->buffer.last++) = value;
+ }
+ /* 0000 0080-0000 07FF -> 110xxxxx 10xxxxxx */
+ else if (value <= 0x7FF) {
+ *(parser->buffer.last++) = 0xC0 + (value >> 6);
+ *(parser->buffer.last++) = 0x80 + (value & 0x3F);
+ }
+ /* 0000 0800-0000 FFFF -> 1110xxxx 10xxxxxx 10xxxxxx */
+ else if (value <= 0xFFFF) {
+ *(parser->buffer.last++) = 0xE0 + (value >> 12);
+ *(parser->buffer.last++) = 0x80 + ((value >> 6) & 0x3F);
+ *(parser->buffer.last++) = 0x80 + (value & 0x3F);
+ }
+ /* 0001 0000-0010 FFFF -> 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
+ else {
+ *(parser->buffer.last++) = 0xF0 + (value >> 18);
+ *(parser->buffer.last++) = 0x80 + ((value >> 12) & 0x3F);
+ *(parser->buffer.last++) = 0x80 + ((value >> 6) & 0x3F);
+ *(parser->buffer.last++) = 0x80 + (value & 0x3F);
+ }
+
+ parser->unread ++;
+ }
+
+ /* On EOF, put NUL into the buffer and return. */
+
+ if (parser->eof) {
+ *(parser->buffer.last++) = '\0';
+ parser->unread ++;
+ return 1;
+ }
+
+ }
+
+ if (parser->offset >= PTRDIFF_MAX)
+ return yaml_parser_set_reader_error(parser, "input is too long",
+ PTRDIFF_MAX, -1);
+
+ return 1;
+}
+
diff --git a/ext/psych/yaml/scanner.c b/ext/psych/yaml/scanner.c
new file mode 100644
index 0000000..08ad8ed
--- /dev/null
+++ b/ext/psych/yaml/scanner.c
@@ -0,0 +1,3583 @@
+
+/*
+ * Introduction
+ * ************
+ *
+ * The following notes assume that you are familiar with the YAML specification
+ * (http://yaml.org/spec/cvs/current.html). We mostly follow it, although in
+ * some cases we are less restrictive that it requires.
+ *
+ * The process of transforming a YAML stream into a sequence of events is
+ * divided on two steps: Scanning and Parsing.
+ *
+ * The Scanner transforms the input stream into a sequence of tokens, while the
+ * parser transform the sequence of tokens produced by the Scanner into a
+ * sequence of parsing events.
+ *
+ * The Scanner is rather clever and complicated. The Parser, on the contrary,
+ * is a straightforward implementation of a recursive-descendant parser (or,
+ * LL(1) parser, as it is usually called).
+ *
+ * Actually there are two issues of Scanning that might be called "clever", the
+ * rest is quite straightforward. The issues are "block collection start" and
+ * "simple keys". Both issues are explained below in details.
+ *
+ * Here the Scanning step is explained and implemented. We start with the list
+ * of all the tokens produced by the Scanner together with short descriptions.
+ *
+ * Now, tokens:
+ *
+ * STREAM-START(encoding) # The stream start.
+ * STREAM-END # The stream end.
+ * VERSION-DIRECTIVE(major,minor) # The '%YAML' directive.
+ * TAG-DIRECTIVE(handle,prefix) # The '%TAG' directive.
+ * DOCUMENT-START # '---'
+ * DOCUMENT-END # '...'
+ * BLOCK-SEQUENCE-START # Indentation increase denoting a block
+ * BLOCK-MAPPING-START # sequence or a block mapping.
+ * BLOCK-END # Indentation decrease.
+ * FLOW-SEQUENCE-START # '['
+ * FLOW-SEQUENCE-END # ']'
+ * BLOCK-SEQUENCE-START # '{'
+ * BLOCK-SEQUENCE-END # '}'
+ * BLOCK-ENTRY # '-'
+ * FLOW-ENTRY # ','
+ * KEY # '?' or nothing (simple keys).
+ * VALUE # ':'
+ * ALIAS(anchor) # '*anchor'
+ * ANCHOR(anchor) # '&anchor'
+ * TAG(handle,suffix) # '!handle!suffix'
+ * SCALAR(value,style) # A scalar.
+ *
+ * The following two tokens are "virtual" tokens denoting the beginning and the
+ * end of the stream:
+ *
+ * STREAM-START(encoding)
+ * STREAM-END
+ *
+ * We pass the information about the input stream encoding with the
+ * STREAM-START token.
+ *
+ * The next two tokens are responsible for tags:
+ *
+ * VERSION-DIRECTIVE(major,minor)
+ * TAG-DIRECTIVE(handle,prefix)
+ *
+ * Example:
+ *
+ * %YAML 1.1
+ * %TAG ! !foo
+ * %TAG !yaml! tag:yaml.org,2002:
+ * ---
+ *
+ * The corresponding sequence of tokens:
+ *
+ * STREAM-START(utf-8)
+ * VERSION-DIRECTIVE(1,1)
+ * TAG-DIRECTIVE("!","!foo")
+ * TAG-DIRECTIVE("!yaml","tag:yaml.org,2002:")
+ * DOCUMENT-START
+ * STREAM-END
+ *
+ * Note that the VERSION-DIRECTIVE and TAG-DIRECTIVE tokens occupy a whole
+ * line.
+ *
+ * The document start and end indicators are represented by:
+ *
+ * DOCUMENT-START
+ * DOCUMENT-END
+ *
+ * Note that if a YAML stream contains an implicit document (without '---'
+ * and '...' indicators), no DOCUMENT-START and DOCUMENT-END tokens will be
+ * produced.
+ *
+ * In the following examples, we present whole documents together with the
+ * produced tokens.
+ *
+ * 1. An implicit document:
+ *
+ * 'a scalar'
+ *
+ * Tokens:
+ *
+ * STREAM-START(utf-8)
+ * SCALAR("a scalar",single-quoted)
+ * STREAM-END
+ *
+ * 2. An explicit document:
+ *
+ * ---
+ * 'a scalar'
+ * ...
+ *
+ * Tokens:
+ *
+ * STREAM-START(utf-8)
+ * DOCUMENT-START
+ * SCALAR("a scalar",single-quoted)
+ * DOCUMENT-END
+ * STREAM-END
+ *
+ * 3. Several documents in a stream:
+ *
+ * 'a scalar'
+ * ---
+ * 'another scalar'
+ * ---
+ * 'yet another scalar'
+ *
+ * Tokens:
+ *
+ * STREAM-START(utf-8)
+ * SCALAR("a scalar",single-quoted)
+ * DOCUMENT-START
+ * SCALAR("another scalar",single-quoted)
+ * DOCUMENT-START
+ * SCALAR("yet another scalar",single-quoted)
+ * STREAM-END
+ *
+ * We have already introduced the SCALAR token above. The following tokens are
+ * used to describe aliases, anchors, tag, and scalars:
+ *
+ * ALIAS(anchor)
+ * ANCHOR(anchor)
+ * TAG(handle,suffix)
+ * SCALAR(value,style)
+ *
+ * The following series of examples illustrate the usage of these tokens:
+ *
+ * 1. A recursive sequence:
+ *
+ * &A [ *A ]
+ *
+ * Tokens:
+ *
+ * STREAM-START(utf-8)
+ * ANCHOR("A")
+ * FLOW-SEQUENCE-START
+ * ALIAS("A")
+ * FLOW-SEQUENCE-END
+ * STREAM-END
+ *
+ * 2. A tagged scalar:
+ *
+ * !!float "3.14" # A good approximation.
+ *
+ * Tokens:
+ *
+ * STREAM-START(utf-8)
+ * TAG("!!","float")
+ * SCALAR("3.14",double-quoted)
+ * STREAM-END
+ *
+ * 3. Various scalar styles:
+ *
+ * --- # Implicit empty plain scalars do not produce tokens.
+ * --- a plain scalar
+ * --- 'a single-quoted scalar'
+ * --- "a double-quoted scalar"
+ * --- |-
+ * a literal scalar
+ * --- >-
+ * a folded
+ * scalar
+ *
+ * Tokens:
+ *
+ * STREAM-START(utf-8)
+ * DOCUMENT-START
+ * DOCUMENT-START
+ * SCALAR("a plain scalar",plain)
+ * DOCUMENT-START
+ * SCALAR("a single-quoted scalar",single-quoted)
+ * DOCUMENT-START
+ * SCALAR("a double-quoted scalar",double-quoted)
+ * DOCUMENT-START
+ * SCALAR("a literal scalar",literal)
+ * DOCUMENT-START
+ * SCALAR("a folded scalar",folded)
+ * STREAM-END
+ *
+ * Now it's time to review collection-related tokens. We will start with
+ * flow collections:
+ *
+ * FLOW-SEQUENCE-START
+ * FLOW-SEQUENCE-END
+ * FLOW-MAPPING-START
+ * FLOW-MAPPING-END
+ * FLOW-ENTRY
+ * KEY
+ * VALUE
+ *
+ * The tokens FLOW-SEQUENCE-START, FLOW-SEQUENCE-END, FLOW-MAPPING-START, and
+ * FLOW-MAPPING-END represent the indicators '[', ']', '{', and '}'
+ * correspondingly. FLOW-ENTRY represent the ',' indicator. Finally the
+ * indicators '?' and ':', which are used for denoting mapping keys and values,
+ * are represented by the KEY and VALUE tokens.
+ *
+ * The following examples show flow collections:
+ *
+ * 1. A flow sequence:
+ *
+ * [item 1, item 2, item 3]
+ *
+ * Tokens:
+ *
+ * STREAM-START(utf-8)
+ * FLOW-SEQUENCE-START
+ * SCALAR("item 1",plain)
+ * FLOW-ENTRY
+ * SCALAR("item 2",plain)
+ * FLOW-ENTRY
+ * SCALAR("item 3",plain)
+ * FLOW-SEQUENCE-END
+ * STREAM-END
+ *
+ * 2. A flow mapping:
+ *
+ * {
+ * a simple key: a value, # Note that the KEY token is produced.
+ * ? a complex key: another value,
+ * }
+ *
+ * Tokens:
+ *
+ * STREAM-START(utf-8)
+ * FLOW-MAPPING-START
+ * KEY
+ * SCALAR("a simple key",plain)
+ * VALUE
+ * SCALAR("a value",plain)
+ * FLOW-ENTRY
+ * KEY
+ * SCALAR("a complex key",plain)
+ * VALUE
+ * SCALAR("another value",plain)
+ * FLOW-ENTRY
+ * FLOW-MAPPING-END
+ * STREAM-END
+ *
+ * A simple key is a key which is not denoted by the '?' indicator. Note that
+ * the Scanner still produce the KEY token whenever it encounters a simple key.
+ *
+ * For scanning block collections, the following tokens are used (note that we
+ * repeat KEY and VALUE here):
+ *
+ * BLOCK-SEQUENCE-START
+ * BLOCK-MAPPING-START
+ * BLOCK-END
+ * BLOCK-ENTRY
+ * KEY
+ * VALUE
+ *
+ * The tokens BLOCK-SEQUENCE-START and BLOCK-MAPPING-START denote indentation
+ * increase that precedes a block collection (cf. the INDENT token in Python).
+ * The token BLOCK-END denote indentation decrease that ends a block collection
+ * (cf. the DEDENT token in Python). However YAML has some syntax pecularities
+ * that makes detections of these tokens more complex.
+ *
+ * The tokens BLOCK-ENTRY, KEY, and VALUE are used to represent the indicators
+ * '-', '?', and ':' correspondingly.
+ *
+ * The following examples show how the tokens BLOCK-SEQUENCE-START,
+ * BLOCK-MAPPING-START, and BLOCK-END are emitted by the Scanner:
+ *
+ * 1. Block sequences:
+ *
+ * - item 1
+ * - item 2
+ * -
+ * - item 3.1
+ * - item 3.2
+ * -
+ * key 1: value 1
+ * key 2: value 2
+ *
+ * Tokens:
+ *
+ * STREAM-START(utf-8)
+ * BLOCK-SEQUENCE-START
+ * BLOCK-ENTRY
+ * SCALAR("item 1",plain)
+ * BLOCK-ENTRY
+ * SCALAR("item 2",plain)
+ * BLOCK-ENTRY
+ * BLOCK-SEQUENCE-START
+ * BLOCK-ENTRY
+ * SCALAR("item 3.1",plain)
+ * BLOCK-ENTRY
+ * SCALAR("item 3.2",plain)
+ * BLOCK-END
+ * BLOCK-ENTRY
+ * BLOCK-MAPPING-START
+ * KEY
+ * SCALAR("key 1",plain)
+ * VALUE
+ * SCALAR("value 1",plain)
+ * KEY
+ * SCALAR("key 2",plain)
+ * VALUE
+ * SCALAR("value 2",plain)
+ * BLOCK-END
+ * BLOCK-END
+ * STREAM-END
+ *
+ * 2. Block mappings:
+ *
+ * a simple key: a value # The KEY token is produced here.
+ * ? a complex key
+ * : another value
+ * a mapping:
+ * key 1: value 1
+ * key 2: value 2
+ * a sequence:
+ * - item 1
+ * - item 2
+ *
+ * Tokens:
+ *
+ * STREAM-START(utf-8)
+ * BLOCK-MAPPING-START
+ * KEY
+ * SCALAR("a simple key",plain)
+ * VALUE
+ * SCALAR("a value",plain)
+ * KEY
+ * SCALAR("a complex key",plain)
+ * VALUE
+ * SCALAR("another value",plain)
+ * KEY
+ * SCALAR("a mapping",plain)
+ * BLOCK-MAPPING-START
+ * KEY
+ * SCALAR("key 1",plain)
+ * VALUE
+ * SCALAR("value 1",plain)
+ * KEY
+ * SCALAR("key 2",plain)
+ * VALUE
+ * SCALAR("value 2",plain)
+ * BLOCK-END
+ * KEY
+ * SCALAR("a sequence",plain)
+ * VALUE
+ * BLOCK-SEQUENCE-START
+ * BLOCK-ENTRY
+ * SCALAR("item 1",plain)
+ * BLOCK-ENTRY
+ * SCALAR("item 2",plain)
+ * BLOCK-END
+ * BLOCK-END
+ * STREAM-END
+ *
+ * YAML does not always require to start a new block collection from a new
+ * line. If the current line contains only '-', '?', and ':' indicators, a new
+ * block collection may start at the current line. The following examples
+ * illustrate this case:
+ *
+ * 1. Collections in a sequence:
+ *
+ * - - item 1
+ * - item 2
+ * - key 1: value 1
+ * key 2: value 2
+ * - ? complex key
+ * : complex value
+ *
+ * Tokens:
+ *
+ * STREAM-START(utf-8)
+ * BLOCK-SEQUENCE-START
+ * BLOCK-ENTRY
+ * BLOCK-SEQUENCE-START
+ * BLOCK-ENTRY
+ * SCALAR("item 1",plain)
+ * BLOCK-ENTRY
+ * SCALAR("item 2",plain)
+ * BLOCK-END
+ * BLOCK-ENTRY
+ * BLOCK-MAPPING-START
+ * KEY
+ * SCALAR("key 1",plain)
+ * VALUE
+ * SCALAR("value 1",plain)
+ * KEY
+ * SCALAR("key 2",plain)
+ * VALUE
+ * SCALAR("value 2",plain)
+ * BLOCK-END
+ * BLOCK-ENTRY
+ * BLOCK-MAPPING-START
+ * KEY
+ * SCALAR("complex key")
+ * VALUE
+ * SCALAR("complex value")
+ * BLOCK-END
+ * BLOCK-END
+ * STREAM-END
+ *
+ * 2. Collections in a mapping:
+ *
+ * ? a sequence
+ * : - item 1
+ * - item 2
+ * ? a mapping
+ * : key 1: value 1
+ * key 2: value 2
+ *
+ * Tokens:
+ *
+ * STREAM-START(utf-8)
+ * BLOCK-MAPPING-START
+ * KEY
+ * SCALAR("a sequence",plain)
+ * VALUE
+ * BLOCK-SEQUENCE-START
+ * BLOCK-ENTRY
+ * SCALAR("item 1",plain)
+ * BLOCK-ENTRY
+ * SCALAR("item 2",plain)
+ * BLOCK-END
+ * KEY
+ * SCALAR("a mapping",plain)
+ * VALUE
+ * BLOCK-MAPPING-START
+ * KEY
+ * SCALAR("key 1",plain)
+ * VALUE
+ * SCALAR("value 1",plain)
+ * KEY
+ * SCALAR("key 2",plain)
+ * VALUE
+ * SCALAR("value 2",plain)
+ * BLOCK-END
+ * BLOCK-END
+ * STREAM-END
+ *
+ * YAML also permits non-indented sequences if they are included into a block
+ * mapping. In this case, the token BLOCK-SEQUENCE-START is not produced:
+ *
+ * key:
+ * - item 1 # BLOCK-SEQUENCE-START is NOT produced here.
+ * - item 2
+ *
+ * Tokens:
+ *
+ * STREAM-START(utf-8)
+ * BLOCK-MAPPING-START
+ * KEY
+ * SCALAR("key",plain)
+ * VALUE
+ * BLOCK-ENTRY
+ * SCALAR("item 1",plain)
+ * BLOCK-ENTRY
+ * SCALAR("item 2",plain)
+ * BLOCK-END
+ */
+
+#include "yaml_private.h"
+
+/*
+ * Ensure that the buffer contains the required number of characters.
+ * Return 1 on success, 0 on failure (reader error or memory error).
+ */
+
+#define CACHE(parser,length) \
+ (parser->unread >= (length) \
+ ? 1 \
+ : yaml_parser_update_buffer(parser, (length)))
+
+/*
+ * Advance the buffer pointer.
+ */
+
+#define SKIP(parser) \
+ (parser->mark.index ++, \
+ parser->mark.column ++, \
+ parser->unread --, \
+ parser->buffer.pointer += WIDTH(parser->buffer))
+
+#define SKIP_LINE(parser) \
+ (IS_CRLF(parser->buffer) ? \
+ (parser->mark.index += 2, \
+ parser->mark.column = 0, \
+ parser->mark.line ++, \
+ parser->unread -= 2, \
+ parser->buffer.pointer += 2) : \
+ IS_BREAK(parser->buffer) ? \
+ (parser->mark.index ++, \
+ parser->mark.column = 0, \
+ parser->mark.line ++, \
+ parser->unread --, \
+ parser->buffer.pointer += WIDTH(parser->buffer)) : 0)
+
+/*
+ * Copy a character to a string buffer and advance pointers.
+ */
+
+#define READ(parser,string) \
+ (STRING_EXTEND(parser,string) ? \
+ (COPY(string,parser->buffer), \
+ parser->mark.index ++, \
+ parser->mark.column ++, \
+ parser->unread --, \
+ 1) : 0)
+
+/*
+ * Copy a line break character to a string buffer and advance pointers.
+ */
+
+#define READ_LINE(parser,string) \
+ (STRING_EXTEND(parser,string) ? \
+ (((CHECK_AT(parser->buffer,'\r',0) \
+ && CHECK_AT(parser->buffer,'\n',1)) ? /* CR LF -> LF */ \
+ (*((string).pointer++) = (yaml_char_t) '\n', \
+ parser->buffer.pointer += 2, \
+ parser->mark.index += 2, \
+ parser->mark.column = 0, \
+ parser->mark.line ++, \
+ parser->unread -= 2) : \
+ (CHECK_AT(parser->buffer,'\r',0) \
+ || CHECK_AT(parser->buffer,'\n',0)) ? /* CR|LF -> LF */ \
+ (*((string).pointer++) = (yaml_char_t) '\n', \
+ parser->buffer.pointer ++, \
+ parser->mark.index ++, \
+ parser->mark.column = 0, \
+ parser->mark.line ++, \
+ parser->unread --) : \
+ (CHECK_AT(parser->buffer,'\xC2',0) \
+ && CHECK_AT(parser->buffer,'\x85',1)) ? /* NEL -> LF */ \
+ (*((string).pointer++) = (yaml_char_t) '\n', \
+ parser->buffer.pointer += 2, \
+ parser->mark.index ++, \
+ parser->mark.column = 0, \
+ parser->mark.line ++, \
+ parser->unread --) : \
+ (CHECK_AT(parser->buffer,'\xE2',0) && \
+ CHECK_AT(parser->buffer,'\x80',1) && \
+ (CHECK_AT(parser->buffer,'\xA8',2) || \
+ CHECK_AT(parser->buffer,'\xA9',2))) ? /* LS|PS -> LS|PS */ \
+ (*((string).pointer++) = *(parser->buffer.pointer++), \
+ *((string).pointer++) = *(parser->buffer.pointer++), \
+ *((string).pointer++) = *(parser->buffer.pointer++), \
+ parser->mark.index ++, \
+ parser->mark.column = 0, \
+ parser->mark.line ++, \
+ parser->unread --) : 0), \
+ 1) : 0)
+
+/*
+ * Public API declarations.
+ */
+
+YAML_DECLARE(int)
+yaml_parser_scan(yaml_parser_t *parser, yaml_token_t *token);
+
+/*
+ * Error handling.
+ */
+
+static int
+yaml_parser_set_scanner_error(yaml_parser_t *parser, const char *context,
+ yaml_mark_t context_mark, const char *problem);
+
+/*
+ * High-level token API.
+ */
+
+YAML_DECLARE(int)
+yaml_parser_fetch_more_tokens(yaml_parser_t *parser);
+
+static int
+yaml_parser_fetch_next_token(yaml_parser_t *parser);
+
+/*
+ * Potential simple keys.
+ */
+
+static int
+yaml_parser_stale_simple_keys(yaml_parser_t *parser);
+
+static int
+yaml_parser_save_simple_key(yaml_parser_t *parser);
+
+static int
+yaml_parser_remove_simple_key(yaml_parser_t *parser);
+
+static int
+yaml_parser_increase_flow_level(yaml_parser_t *parser);
+
+static int
+yaml_parser_decrease_flow_level(yaml_parser_t *parser);
+
+/*
+ * Indentation treatment.
+ */
+
+static int
+yaml_parser_roll_indent(yaml_parser_t *parser, ptrdiff_t column,
+ ptrdiff_t number, yaml_token_type_t type, yaml_mark_t mark);
+
+static int
+yaml_parser_unroll_indent(yaml_parser_t *parser, ptrdiff_t column);
+
+/*
+ * Token fetchers.
+ */
+
+static int
+yaml_parser_fetch_stream_start(yaml_parser_t *parser);
+
+static int
+yaml_parser_fetch_stream_end(yaml_parser_t *parser);
+
+static int
+yaml_parser_fetch_directive(yaml_parser_t *parser);
+
+static int
+yaml_parser_fetch_document_indicator(yaml_parser_t *parser,
+ yaml_token_type_t type);
+
+static int
+yaml_parser_fetch_flow_collection_start(yaml_parser_t *parser,
+ yaml_token_type_t type);
+
+static int
+yaml_parser_fetch_flow_collection_end(yaml_parser_t *parser,
+ yaml_token_type_t type);
+
+static int
+yaml_parser_fetch_flow_entry(yaml_parser_t *parser);
+
+static int
+yaml_parser_fetch_block_entry(yaml_parser_t *parser);
+
+static int
+yaml_parser_fetch_key(yaml_parser_t *parser);
+
+static int
+yaml_parser_fetch_value(yaml_parser_t *parser);
+
+static int
+yaml_parser_fetch_anchor(yaml_parser_t *parser, yaml_token_type_t type);
+
+static int
+yaml_parser_fetch_tag(yaml_parser_t *parser);
+
+static int
+yaml_parser_fetch_block_scalar(yaml_parser_t *parser, int literal);
+
+static int
+yaml_parser_fetch_flow_scalar(yaml_parser_t *parser, int single);
+
+static int
+yaml_parser_fetch_plain_scalar(yaml_parser_t *parser);
+
+/*
+ * Token scanners.
+ */
+
+static int
+yaml_parser_scan_to_next_token(yaml_parser_t *parser);
+
+static int
+yaml_parser_scan_directive(yaml_parser_t *parser, yaml_token_t *token);
+
+static int
+yaml_parser_scan_directive_name(yaml_parser_t *parser,
+ yaml_mark_t start_mark, yaml_char_t **name);
+
+static int
+yaml_parser_scan_version_directive_value(yaml_parser_t *parser,
+ yaml_mark_t start_mark, int *major, int *minor);
+
+static int
+yaml_parser_scan_version_directive_number(yaml_parser_t *parser,
+ yaml_mark_t start_mark, int *number);
+
+static int
+yaml_parser_scan_tag_directive_value(yaml_parser_t *parser,
+ yaml_mark_t mark, yaml_char_t **handle, yaml_char_t **prefix);
+
+static int
+yaml_parser_scan_anchor(yaml_parser_t *parser, yaml_token_t *token,
+ yaml_token_type_t type);
+
+static int
+yaml_parser_scan_tag(yaml_parser_t *parser, yaml_token_t *token);
+
+static int
+yaml_parser_scan_tag_handle(yaml_parser_t *parser, int directive,
+ yaml_mark_t start_mark, yaml_char_t **handle);
+
+static int
+yaml_parser_scan_tag_uri(yaml_parser_t *parser, int directive,
+ yaml_char_t *head, yaml_mark_t start_mark, yaml_char_t **uri);
+
+static int
+yaml_parser_scan_uri_escapes(yaml_parser_t *parser, int directive,
+ yaml_mark_t start_mark, yaml_string_t *string);
+
+static int
+yaml_parser_scan_block_scalar(yaml_parser_t *parser, yaml_token_t *token,
+ int literal);
+
+static int
+yaml_parser_scan_block_scalar_breaks(yaml_parser_t *parser,
+ int *indent, yaml_string_t *breaks,
+ yaml_mark_t start_mark, yaml_mark_t *end_mark);
+
+static int
+yaml_parser_scan_flow_scalar(yaml_parser_t *parser, yaml_token_t *token,
+ int single);
+
+static int
+yaml_parser_scan_plain_scalar(yaml_parser_t *parser, yaml_token_t *token);
+
+/*
+ * Get the next token.
+ */
+
+YAML_DECLARE(int)
+yaml_parser_scan(yaml_parser_t *parser, yaml_token_t *token)
+{
+ assert(parser); /* Non-NULL parser object is expected. */
+ assert(token); /* Non-NULL token object is expected. */
+
+ /* Erase the token object. */
+
+ memset(token, 0, sizeof(yaml_token_t));
+
+ /* No tokens after STREAM-END or error. */
+
+ if (parser->stream_end_produced || parser->error) {
+ return 1;
+ }
+
+ /* Ensure that the tokens queue contains enough tokens. */
+
+ if (!parser->token_available) {
+ if (!yaml_parser_fetch_more_tokens(parser))
+ return 0;
+ }
+
+ /* Fetch the next token from the queue. */
+
+ *token = DEQUEUE(parser, parser->tokens);
+ parser->token_available = 0;
+ parser->tokens_parsed ++;
+
+ if (token->type == YAML_STREAM_END_TOKEN) {
+ parser->stream_end_produced = 1;
+ }
+
+ return 1;
+}
+
+/*
+ * Set the scanner error and return 0.
+ */
+
+static int
+yaml_parser_set_scanner_error(yaml_parser_t *parser, const char *context,
+ yaml_mark_t context_mark, const char *problem)
+{
+ parser->error = YAML_SCANNER_ERROR;
+ parser->context = context;
+ parser->context_mark = context_mark;
+ parser->problem = problem;
+ parser->problem_mark = parser->mark;
+
+ return 0;
+}
+
+/*
+ * Ensure that the tokens queue contains at least one token which can be
+ * returned to the Parser.
+ */
+
+YAML_DECLARE(int)
+yaml_parser_fetch_more_tokens(yaml_parser_t *parser)
+{
+ int need_more_tokens;
+
+ /* While we need more tokens to fetch, do it. */
+
+ while (1)
+ {
+ /*
+ * Check if we really need to fetch more tokens.
+ */
+
+ need_more_tokens = 0;
+
+ if (parser->tokens.head == parser->tokens.tail)
+ {
+ /* Queue is empty. */
+
+ need_more_tokens = 1;
+ }
+ else
+ {
+ yaml_simple_key_t *simple_key;
+
+ /* Check if any potential simple key may occupy the head position. */
+
+ if (!yaml_parser_stale_simple_keys(parser))
+ return 0;
+
+ for (simple_key = parser->simple_keys.start;
+ simple_key != parser->simple_keys.top; simple_key++) {
+ if (simple_key->possible
+ && simple_key->token_number == parser->tokens_parsed) {
+ need_more_tokens = 1;
+ break;
+ }
+ }
+ }
+
+ /* We are finished. */
+
+ if (!need_more_tokens)
+ break;
+
+ /* Fetch the next token. */
+
+ if (!yaml_parser_fetch_next_token(parser))
+ return 0;
+ }
+
+ parser->token_available = 1;
+
+ return 1;
+}
+
+/*
+ * The dispatcher for token fetchers.
+ */
+
+static int
+yaml_parser_fetch_next_token(yaml_parser_t *parser)
+{
+ /* Ensure that the buffer is initialized. */
+
+ if (!CACHE(parser, 1))
+ return 0;
+
+ /* Check if we just started scanning. Fetch STREAM-START then. */
+
+ if (!parser->stream_start_produced)
+ return yaml_parser_fetch_stream_start(parser);
+
+ /* Eat whitespaces and comments until we reach the next token. */
+
+ if (!yaml_parser_scan_to_next_token(parser))
+ return 0;
+
+ /* Remove obsolete potential simple keys. */
+
+ if (!yaml_parser_stale_simple_keys(parser))
+ return 0;
+
+ /* Check the indentation level against the current column. */
+
+ if (!yaml_parser_unroll_indent(parser, parser->mark.column))
+ return 0;
+
+ /*
+ * Ensure that the buffer contains at least 4 characters. 4 is the length
+ * of the longest indicators ('--- ' and '... ').
+ */
+
+ if (!CACHE(parser, 4))
+ return 0;
+
+ /* Is it the end of the stream? */
+
+ if (IS_Z(parser->buffer))
+ return yaml_parser_fetch_stream_end(parser);
+
+ /* Is it a directive? */
+
+ if (parser->mark.column == 0 && CHECK(parser->buffer, '%'))
+ return yaml_parser_fetch_directive(parser);
+
+ /* Is it the document start indicator? */
+
+ if (parser->mark.column == 0
+ && CHECK_AT(parser->buffer, '-', 0)
+ && CHECK_AT(parser->buffer, '-', 1)
+ && CHECK_AT(parser->buffer, '-', 2)
+ && IS_BLANKZ_AT(parser->buffer, 3))
+ return yaml_parser_fetch_document_indicator(parser,
+ YAML_DOCUMENT_START_TOKEN);
+
+ /* Is it the document end indicator? */
+
+ if (parser->mark.column == 0
+ && CHECK_AT(parser->buffer, '.', 0)
+ && CHECK_AT(parser->buffer, '.', 1)
+ && CHECK_AT(parser->buffer, '.', 2)
+ && IS_BLANKZ_AT(parser->buffer, 3))
+ return yaml_parser_fetch_document_indicator(parser,
+ YAML_DOCUMENT_END_TOKEN);
+
+ /* Is it the flow sequence start indicator? */
+
+ if (CHECK(parser->buffer, '['))
+ return yaml_parser_fetch_flow_collection_start(parser,
+ YAML_FLOW_SEQUENCE_START_TOKEN);
+
+ /* Is it the flow mapping start indicator? */
+
+ if (CHECK(parser->buffer, '{'))
+ return yaml_parser_fetch_flow_collection_start(parser,
+ YAML_FLOW_MAPPING_START_TOKEN);
+
+ /* Is it the flow sequence end indicator? */
+
+ if (CHECK(parser->buffer, ']'))
+ return yaml_parser_fetch_flow_collection_end(parser,
+ YAML_FLOW_SEQUENCE_END_TOKEN);
+
+ /* Is it the flow mapping end indicator? */
+
+ if (CHECK(parser->buffer, '}'))
+ return yaml_parser_fetch_flow_collection_end(parser,
+ YAML_FLOW_MAPPING_END_TOKEN);
+
+ /* Is it the flow entry indicator? */
+
+ if (CHECK(parser->buffer, ','))
+ return yaml_parser_fetch_flow_entry(parser);
+
+ /* Is it the block entry indicator? */
+
+ if (CHECK(parser->buffer, '-') && IS_BLANKZ_AT(parser->buffer, 1))
+ return yaml_parser_fetch_block_entry(parser);
+
+ /* Is it the key indicator? */
+
+ if (CHECK(parser->buffer, '?')
+ && (parser->flow_level || IS_BLANKZ_AT(parser->buffer, 1)))
+ return yaml_parser_fetch_key(parser);
+
+ /* Is it the value indicator? */
+
+ if (CHECK(parser->buffer, ':')
+ && (parser->flow_level || IS_BLANKZ_AT(parser->buffer, 1)))
+ return yaml_parser_fetch_value(parser);
+
+ /* Is it an alias? */
+
+ if (CHECK(parser->buffer, '*'))
+ return yaml_parser_fetch_anchor(parser, YAML_ALIAS_TOKEN);
+
+ /* Is it an anchor? */
+
+ if (CHECK(parser->buffer, '&'))
+ return yaml_parser_fetch_anchor(parser, YAML_ANCHOR_TOKEN);
+
+ /* Is it a tag? */
+
+ if (CHECK(parser->buffer, '!'))
+ return yaml_parser_fetch_tag(parser);
+
+ /* Is it a literal scalar? */
+
+ if (CHECK(parser->buffer, '|') && !parser->flow_level)
+ return yaml_parser_fetch_block_scalar(parser, 1);
+
+ /* Is it a folded scalar? */
+
+ if (CHECK(parser->buffer, '>') && !parser->flow_level)
+ return yaml_parser_fetch_block_scalar(parser, 0);
+
+ /* Is it a single-quoted scalar? */
+
+ if (CHECK(parser->buffer, '\''))
+ return yaml_parser_fetch_flow_scalar(parser, 1);
+
+ /* Is it a double-quoted scalar? */
+
+ if (CHECK(parser->buffer, '"'))
+ return yaml_parser_fetch_flow_scalar(parser, 0);
+
+ /*
+ * Is it a plain scalar?
+ *
+ * A plain scalar may start with any non-blank characters except
+ *
+ * '-', '?', ':', ',', '[', ']', '{', '}',
+ * '#', '&', '*', '!', '|', '>', '\'', '\"',
+ * '%', '@', '`'.
+ *
+ * In the block context (and, for the '-' indicator, in the flow context
+ * too), it may also start with the characters
+ *
+ * '-', '?', ':'
+ *
+ * if it is followed by a non-space character.
+ *
+ * The last rule is more restrictive than the specification requires.
+ */
+
+ if (!(IS_BLANKZ(parser->buffer) || CHECK(parser->buffer, '-')
+ || CHECK(parser->buffer, '?') || CHECK(parser->buffer, ':')
+ || CHECK(parser->buffer, ',') || CHECK(parser->buffer, '[')
+ || CHECK(parser->buffer, ']') || CHECK(parser->buffer, '{')
+ || CHECK(parser->buffer, '}') || CHECK(parser->buffer, '#')
+ || CHECK(parser->buffer, '&') || CHECK(parser->buffer, '*')
+ || CHECK(parser->buffer, '!') || CHECK(parser->buffer, '|')
+ || CHECK(parser->buffer, '>') || CHECK(parser->buffer, '\'')
+ || CHECK(parser->buffer, '"') || CHECK(parser->buffer, '%')
+ || CHECK(parser->buffer, '@') || CHECK(parser->buffer, '`')) ||
+ (CHECK(parser->buffer, '-') && !IS_BLANK_AT(parser->buffer, 1)) ||
+ (!parser->flow_level &&
+ (CHECK(parser->buffer, '?') || CHECK(parser->buffer, ':'))
+ && !IS_BLANKZ_AT(parser->buffer, 1)))
+ return yaml_parser_fetch_plain_scalar(parser);
+
+ /*
+ * If we don't determine the token type so far, it is an error.
+ */
+
+ return yaml_parser_set_scanner_error(parser,
+ "while scanning for the next token", parser->mark,
+ "found character that cannot start any token");
+}
+
+/*
+ * Check the list of potential simple keys and remove the positions that
+ * cannot contain simple keys anymore.
+ */
+
+static int
+yaml_parser_stale_simple_keys(yaml_parser_t *parser)
+{
+ yaml_simple_key_t *simple_key;
+
+ /* Check for a potential simple key for each flow level. */
+
+ for (simple_key = parser->simple_keys.start;
+ simple_key != parser->simple_keys.top; simple_key ++)
+ {
+ /*
+ * The specification requires that a simple key
+ *
+ * - is limited to a single line,
+ * - is shorter than 1024 characters.
+ */
+
+ if (simple_key->possible
+ && (simple_key->mark.line < parser->mark.line
+ || simple_key->mark.index+1024 < parser->mark.index)) {
+
+ /* Check if the potential simple key to be removed is required. */
+
+ if (simple_key->required) {
+ return yaml_parser_set_scanner_error(parser,
+ "while scanning a simple key", simple_key->mark,
+ "could not find expected ':'");
+ }
+
+ simple_key->possible = 0;
+ }
+ }
+
+ return 1;
+}
+
+/*
+ * Check if a simple key may start at the current position and add it if
+ * needed.
+ */
+
+static int
+yaml_parser_save_simple_key(yaml_parser_t *parser)
+{
+ /*
+ * A simple key is required at the current position if the scanner is in
+ * the block context and the current column coincides with the indentation
+ * level.
+ */
+
+ int required = (!parser->flow_level
+ && parser->indent == (ptrdiff_t)parser->mark.column);
+
+ /*
+ * A simple key is required only when it is the first token in the current
+ * line. Therefore it is always allowed. But we add a check anyway.
+ */
+
+ assert(parser->simple_key_allowed || !required); /* Impossible. */
+
+ /*
+ * If the current position may start a simple key, save it.
+ */
+
+ if (parser->simple_key_allowed)
+ {
+ yaml_simple_key_t simple_key;
+ simple_key.possible = 1;
+ simple_key.required = required;
+ simple_key.token_number =
+ parser->tokens_parsed + (parser->tokens.tail - parser->tokens.head);
+ simple_key.mark = parser->mark;
+
+ if (!yaml_parser_remove_simple_key(parser)) return 0;
+
+ *(parser->simple_keys.top-1) = simple_key;
+ }
+
+ return 1;
+}
+
+/*
+ * Remove a potential simple key at the current flow level.
+ */
+
+static int
+yaml_parser_remove_simple_key(yaml_parser_t *parser)
+{
+ yaml_simple_key_t *simple_key = parser->simple_keys.top-1;
+
+ if (simple_key->possible)
+ {
+ /* If the key is required, it is an error. */
+
+ if (simple_key->required) {
+ return yaml_parser_set_scanner_error(parser,
+ "while scanning a simple key", simple_key->mark,
+ "could not find expected ':'");
+ }
+ }
+
+ /* Remove the key from the stack. */
+
+ simple_key->possible = 0;
+
+ return 1;
+}
+
+/*
+ * Increase the flow level and resize the simple key list if needed.
+ */
+
+static int
+yaml_parser_increase_flow_level(yaml_parser_t *parser)
+{
+ yaml_simple_key_t empty_simple_key = { 0, 0, 0, { 0, 0, 0 } };
+
+ /* Reset the simple key on the next level. */
+
+ if (!PUSH(parser, parser->simple_keys, empty_simple_key))
+ return 0;
+
+ /* Increase the flow level. */
+
+ if (parser->flow_level == INT_MAX) {
+ parser->error = YAML_MEMORY_ERROR;
+ return 0;
+ }
+
+ parser->flow_level++;
+
+ return 1;
+}
+
+/*
+ * Decrease the flow level.
+ */
+
+static int
+yaml_parser_decrease_flow_level(yaml_parser_t *parser)
+{
+ if (parser->flow_level) {
+ parser->flow_level --;
+ (void)POP(parser, parser->simple_keys);
+ }
+
+ return 1;
+}
+
+/*
+ * Push the current indentation level to the stack and set the new level
+ * the current column is greater than the indentation level. In this case,
+ * append or insert the specified token into the token queue.
+ *
+ */
+
+static int
+yaml_parser_roll_indent(yaml_parser_t *parser, ptrdiff_t column,
+ ptrdiff_t number, yaml_token_type_t type, yaml_mark_t mark)
+{
+ yaml_token_t token;
+
+ /* In the flow context, do nothing. */
+
+ if (parser->flow_level)
+ return 1;
+
+ if (parser->indent < column)
+ {
+ /*
+ * Push the current indentation level to the stack and set the new
+ * indentation level.
+ */
+
+ if (!PUSH(parser, parser->indents, parser->indent))
+ return 0;
+
+#if PTRDIFF_MAX > INT_MAX
+ if (column > INT_MAX) {
+ parser->error = YAML_MEMORY_ERROR;
+ return 0;
+ }
+#endif
+
+ parser->indent = (int)column;
+
+ /* Create a token and insert it into the queue. */
+
+ TOKEN_INIT(token, type, mark, mark);
+
+ if (number == -1) {
+ if (!ENQUEUE(parser, parser->tokens, token))
+ return 0;
+ }
+ else {
+ if (!QUEUE_INSERT(parser,
+ parser->tokens, number - parser->tokens_parsed, token))
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/*
+ * Pop indentation levels from the indents stack until the current level
+ * becomes less or equal to the column. For each intendation level, append
+ * the BLOCK-END token.
+ */
+
+
+static int
+yaml_parser_unroll_indent(yaml_parser_t *parser, ptrdiff_t column)
+{
+ yaml_token_t token;
+
+ /* In the flow context, do nothing. */
+
+ if (parser->flow_level)
+ return 1;
+
+ /* Loop through the intendation levels in the stack. */
+
+ while (parser->indent > column)
+ {
+ /* Create a token and append it to the queue. */
+
+ TOKEN_INIT(token, YAML_BLOCK_END_TOKEN, parser->mark, parser->mark);
+
+ if (!ENQUEUE(parser, parser->tokens, token))
+ return 0;
+
+ /* Pop the indentation level. */
+
+ parser->indent = POP(parser, parser->indents);
+ }
+
+ return 1;
+}
+
+/*
+ * Initialize the scanner and produce the STREAM-START token.
+ */
+
+static int
+yaml_parser_fetch_stream_start(yaml_parser_t *parser)
+{
+ yaml_simple_key_t simple_key = { 0, 0, 0, { 0, 0, 0 } };
+ yaml_token_t token;
+
+ /* Set the initial indentation. */
+
+ parser->indent = -1;
+
+ /* Initialize the simple key stack. */
+
+ if (!PUSH(parser, parser->simple_keys, simple_key))
+ return 0;
+
+ /* A simple key is allowed at the beginning of the stream. */
+
+ parser->simple_key_allowed = 1;
+
+ /* We have started. */
+
+ parser->stream_start_produced = 1;
+
+ /* Create the STREAM-START token and append it to the queue. */
+
+ STREAM_START_TOKEN_INIT(token, parser->encoding,
+ parser->mark, parser->mark);
+
+ if (!ENQUEUE(parser, parser->tokens, token))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Produce the STREAM-END token and shut down the scanner.
+ */
+
+static int
+yaml_parser_fetch_stream_end(yaml_parser_t *parser)
+{
+ yaml_token_t token;
+
+ /* Force new line. */
+
+ if (parser->mark.column != 0) {
+ parser->mark.column = 0;
+ parser->mark.line ++;
+ }
+
+ /* Reset the indentation level. */
+
+ if (!yaml_parser_unroll_indent(parser, -1))
+ return 0;
+
+ /* Reset simple keys. */
+
+ if (!yaml_parser_remove_simple_key(parser))
+ return 0;
+
+ parser->simple_key_allowed = 0;
+
+ /* Create the STREAM-END token and append it to the queue. */
+
+ STREAM_END_TOKEN_INIT(token, parser->mark, parser->mark);
+
+ if (!ENQUEUE(parser, parser->tokens, token))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Produce a VERSION-DIRECTIVE or TAG-DIRECTIVE token.
+ */
+
+static int
+yaml_parser_fetch_directive(yaml_parser_t *parser)
+{
+ yaml_token_t token;
+
+ /* Reset the indentation level. */
+
+ if (!yaml_parser_unroll_indent(parser, -1))
+ return 0;
+
+ /* Reset simple keys. */
+
+ if (!yaml_parser_remove_simple_key(parser))
+ return 0;
+
+ parser->simple_key_allowed = 0;
+
+ /* Create the YAML-DIRECTIVE or TAG-DIRECTIVE token. */
+
+ if (!yaml_parser_scan_directive(parser, &token))
+ return 0;
+
+ /* Append the token to the queue. */
+
+ if (!ENQUEUE(parser, parser->tokens, token)) {
+ yaml_token_delete(&token);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Produce the DOCUMENT-START or DOCUMENT-END token.
+ */
+
+static int
+yaml_parser_fetch_document_indicator(yaml_parser_t *parser,
+ yaml_token_type_t type)
+{
+ yaml_mark_t start_mark, end_mark;
+ yaml_token_t token;
+
+ /* Reset the indentation level. */
+
+ if (!yaml_parser_unroll_indent(parser, -1))
+ return 0;
+
+ /* Reset simple keys. */
+
+ if (!yaml_parser_remove_simple_key(parser))
+ return 0;
+
+ parser->simple_key_allowed = 0;
+
+ /* Consume the token. */
+
+ start_mark = parser->mark;
+
+ SKIP(parser);
+ SKIP(parser);
+ SKIP(parser);
+
+ end_mark = parser->mark;
+
+ /* Create the DOCUMENT-START or DOCUMENT-END token. */
+
+ TOKEN_INIT(token, type, start_mark, end_mark);
+
+ /* Append the token to the queue. */
+
+ if (!ENQUEUE(parser, parser->tokens, token))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Produce the FLOW-SEQUENCE-START or FLOW-MAPPING-START token.
+ */
+
+static int
+yaml_parser_fetch_flow_collection_start(yaml_parser_t *parser,
+ yaml_token_type_t type)
+{
+ yaml_mark_t start_mark, end_mark;
+ yaml_token_t token;
+
+ /* The indicators '[' and '{' may start a simple key. */
+
+ if (!yaml_parser_save_simple_key(parser))
+ return 0;
+
+ /* Increase the flow level. */
+
+ if (!yaml_parser_increase_flow_level(parser))
+ return 0;
+
+ /* A simple key may follow the indicators '[' and '{'. */
+
+ parser->simple_key_allowed = 1;
+
+ /* Consume the token. */
+
+ start_mark = parser->mark;
+ SKIP(parser);
+ end_mark = parser->mark;
+
+ /* Create the FLOW-SEQUENCE-START of FLOW-MAPPING-START token. */
+
+ TOKEN_INIT(token, type, start_mark, end_mark);
+
+ /* Append the token to the queue. */
+
+ if (!ENQUEUE(parser, parser->tokens, token))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Produce the FLOW-SEQUENCE-END or FLOW-MAPPING-END token.
+ */
+
+static int
+yaml_parser_fetch_flow_collection_end(yaml_parser_t *parser,
+ yaml_token_type_t type)
+{
+ yaml_mark_t start_mark, end_mark;
+ yaml_token_t token;
+
+ /* Reset any potential simple key on the current flow level. */
+
+ if (!yaml_parser_remove_simple_key(parser))
+ return 0;
+
+ /* Decrease the flow level. */
+
+ if (!yaml_parser_decrease_flow_level(parser))
+ return 0;
+
+ /* No simple keys after the indicators ']' and '}'. */
+
+ parser->simple_key_allowed = 0;
+
+ /* Consume the token. */
+
+ start_mark = parser->mark;
+ SKIP(parser);
+ end_mark = parser->mark;
+
+ /* Create the FLOW-SEQUENCE-END of FLOW-MAPPING-END token. */
+
+ TOKEN_INIT(token, type, start_mark, end_mark);
+
+ /* Append the token to the queue. */
+
+ if (!ENQUEUE(parser, parser->tokens, token))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Produce the FLOW-ENTRY token.
+ */
+
+static int
+yaml_parser_fetch_flow_entry(yaml_parser_t *parser)
+{
+ yaml_mark_t start_mark, end_mark;
+ yaml_token_t token;
+
+ /* Reset any potential simple keys on the current flow level. */
+
+ if (!yaml_parser_remove_simple_key(parser))
+ return 0;
+
+ /* Simple keys are allowed after ','. */
+
+ parser->simple_key_allowed = 1;
+
+ /* Consume the token. */
+
+ start_mark = parser->mark;
+ SKIP(parser);
+ end_mark = parser->mark;
+
+ /* Create the FLOW-ENTRY token and append it to the queue. */
+
+ TOKEN_INIT(token, YAML_FLOW_ENTRY_TOKEN, start_mark, end_mark);
+
+ if (!ENQUEUE(parser, parser->tokens, token))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Produce the BLOCK-ENTRY token.
+ */
+
+static int
+yaml_parser_fetch_block_entry(yaml_parser_t *parser)
+{
+ yaml_mark_t start_mark, end_mark;
+ yaml_token_t token;
+
+ /* Check if the scanner is in the block context. */
+
+ if (!parser->flow_level)
+ {
+ /* Check if we are allowed to start a new entry. */
+
+ if (!parser->simple_key_allowed) {
+ return yaml_parser_set_scanner_error(parser, NULL, parser->mark,
+ "block sequence entries are not allowed in this context");
+ }
+
+ /* Add the BLOCK-SEQUENCE-START token if needed. */
+
+ if (!yaml_parser_roll_indent(parser, parser->mark.column, -1,
+ YAML_BLOCK_SEQUENCE_START_TOKEN, parser->mark))
+ return 0;
+ }
+ else
+ {
+ /*
+ * It is an error for the '-' indicator to occur in the flow context,
+ * but we let the Parser detect and report about it because the Parser
+ * is able to point to the context.
+ */
+ }
+
+ /* Reset any potential simple keys on the current flow level. */
+
+ if (!yaml_parser_remove_simple_key(parser))
+ return 0;
+
+ /* Simple keys are allowed after '-'. */
+
+ parser->simple_key_allowed = 1;
+
+ /* Consume the token. */
+
+ start_mark = parser->mark;
+ SKIP(parser);
+ end_mark = parser->mark;
+
+ /* Create the BLOCK-ENTRY token and append it to the queue. */
+
+ TOKEN_INIT(token, YAML_BLOCK_ENTRY_TOKEN, start_mark, end_mark);
+
+ if (!ENQUEUE(parser, parser->tokens, token))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Produce the KEY token.
+ */
+
+static int
+yaml_parser_fetch_key(yaml_parser_t *parser)
+{
+ yaml_mark_t start_mark, end_mark;
+ yaml_token_t token;
+
+ /* In the block context, additional checks are required. */
+
+ if (!parser->flow_level)
+ {
+ /* Check if we are allowed to start a new key (not nessesary simple). */
+
+ if (!parser->simple_key_allowed) {
+ return yaml_parser_set_scanner_error(parser, NULL, parser->mark,
+ "mapping keys are not allowed in this context");
+ }
+
+ /* Add the BLOCK-MAPPING-START token if needed. */
+
+ if (!yaml_parser_roll_indent(parser, parser->mark.column, -1,
+ YAML_BLOCK_MAPPING_START_TOKEN, parser->mark))
+ return 0;
+ }
+
+ /* Reset any potential simple keys on the current flow level. */
+
+ if (!yaml_parser_remove_simple_key(parser))
+ return 0;
+
+ /* Simple keys are allowed after '?' in the block context. */
+
+ parser->simple_key_allowed = (!parser->flow_level);
+
+ /* Consume the token. */
+
+ start_mark = parser->mark;
+ SKIP(parser);
+ end_mark = parser->mark;
+
+ /* Create the KEY token and append it to the queue. */
+
+ TOKEN_INIT(token, YAML_KEY_TOKEN, start_mark, end_mark);
+
+ if (!ENQUEUE(parser, parser->tokens, token))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Produce the VALUE token.
+ */
+
+static int
+yaml_parser_fetch_value(yaml_parser_t *parser)
+{
+ yaml_mark_t start_mark, end_mark;
+ yaml_token_t token;
+ yaml_simple_key_t *simple_key = parser->simple_keys.top-1;
+
+ /* Have we found a simple key? */
+
+ if (simple_key->possible)
+ {
+
+ /* Create the KEY token and insert it into the queue. */
+
+ TOKEN_INIT(token, YAML_KEY_TOKEN, simple_key->mark, simple_key->mark);
+
+ if (!QUEUE_INSERT(parser, parser->tokens,
+ simple_key->token_number - parser->tokens_parsed, token))
+ return 0;
+
+ /* In the block context, we may need to add the BLOCK-MAPPING-START token. */
+
+ if (!yaml_parser_roll_indent(parser, simple_key->mark.column,
+ simple_key->token_number,
+ YAML_BLOCK_MAPPING_START_TOKEN, simple_key->mark))
+ return 0;
+
+ /* Remove the simple key. */
+
+ simple_key->possible = 0;
+
+ /* A simple key cannot follow another simple key. */
+
+ parser->simple_key_allowed = 0;
+ }
+ else
+ {
+ /* The ':' indicator follows a complex key. */
+
+ /* In the block context, extra checks are required. */
+
+ if (!parser->flow_level)
+ {
+ /* Check if we are allowed to start a complex value. */
+
+ if (!parser->simple_key_allowed) {
+ return yaml_parser_set_scanner_error(parser, NULL, parser->mark,
+ "mapping values are not allowed in this context");
+ }
+
+ /* Add the BLOCK-MAPPING-START token if needed. */
+
+ if (!yaml_parser_roll_indent(parser, parser->mark.column, -1,
+ YAML_BLOCK_MAPPING_START_TOKEN, parser->mark))
+ return 0;
+ }
+
+ /* Simple keys after ':' are allowed in the block context. */
+
+ parser->simple_key_allowed = (!parser->flow_level);
+ }
+
+ /* Consume the token. */
+
+ start_mark = parser->mark;
+ SKIP(parser);
+ end_mark = parser->mark;
+
+ /* Create the VALUE token and append it to the queue. */
+
+ TOKEN_INIT(token, YAML_VALUE_TOKEN, start_mark, end_mark);
+
+ if (!ENQUEUE(parser, parser->tokens, token))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Produce the ALIAS or ANCHOR token.
+ */
+
+static int
+yaml_parser_fetch_anchor(yaml_parser_t *parser, yaml_token_type_t type)
+{
+ yaml_token_t token;
+
+ /* An anchor or an alias could be a simple key. */
+
+ if (!yaml_parser_save_simple_key(parser))
+ return 0;
+
+ /* A simple key cannot follow an anchor or an alias. */
+
+ parser->simple_key_allowed = 0;
+
+ /* Create the ALIAS or ANCHOR token and append it to the queue. */
+
+ if (!yaml_parser_scan_anchor(parser, &token, type))
+ return 0;
+
+ if (!ENQUEUE(parser, parser->tokens, token)) {
+ yaml_token_delete(&token);
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Produce the TAG token.
+ */
+
+static int
+yaml_parser_fetch_tag(yaml_parser_t *parser)
+{
+ yaml_token_t token;
+
+ /* A tag could be a simple key. */
+
+ if (!yaml_parser_save_simple_key(parser))
+ return 0;
+
+ /* A simple key cannot follow a tag. */
+
+ parser->simple_key_allowed = 0;
+
+ /* Create the TAG token and append it to the queue. */
+
+ if (!yaml_parser_scan_tag(parser, &token))
+ return 0;
+
+ if (!ENQUEUE(parser, parser->tokens, token)) {
+ yaml_token_delete(&token);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Produce the SCALAR(...,literal) or SCALAR(...,folded) tokens.
+ */
+
+static int
+yaml_parser_fetch_block_scalar(yaml_parser_t *parser, int literal)
+{
+ yaml_token_t token;
+
+ /* Remove any potential simple keys. */
+
+ if (!yaml_parser_remove_simple_key(parser))
+ return 0;
+
+ /* A simple key may follow a block scalar. */
+
+ parser->simple_key_allowed = 1;
+
+ /* Create the SCALAR token and append it to the queue. */
+
+ if (!yaml_parser_scan_block_scalar(parser, &token, literal))
+ return 0;
+
+ if (!ENQUEUE(parser, parser->tokens, token)) {
+ yaml_token_delete(&token);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Produce the SCALAR(...,single-quoted) or SCALAR(...,double-quoted) tokens.
+ */
+
+static int
+yaml_parser_fetch_flow_scalar(yaml_parser_t *parser, int single)
+{
+ yaml_token_t token;
+
+ /* A plain scalar could be a simple key. */
+
+ if (!yaml_parser_save_simple_key(parser))
+ return 0;
+
+ /* A simple key cannot follow a flow scalar. */
+
+ parser->simple_key_allowed = 0;
+
+ /* Create the SCALAR token and append it to the queue. */
+
+ if (!yaml_parser_scan_flow_scalar(parser, &token, single))
+ return 0;
+
+ if (!ENQUEUE(parser, parser->tokens, token)) {
+ yaml_token_delete(&token);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Produce the SCALAR(...,plain) token.
+ */
+
+static int
+yaml_parser_fetch_plain_scalar(yaml_parser_t *parser)
+{
+ yaml_token_t token;
+
+ /* A plain scalar could be a simple key. */
+
+ if (!yaml_parser_save_simple_key(parser))
+ return 0;
+
+ /* A simple key cannot follow a flow scalar. */
+
+ parser->simple_key_allowed = 0;
+
+ /* Create the SCALAR token and append it to the queue. */
+
+ if (!yaml_parser_scan_plain_scalar(parser, &token))
+ return 0;
+
+ if (!ENQUEUE(parser, parser->tokens, token)) {
+ yaml_token_delete(&token);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Eat whitespaces and comments until the next token is found.
+ */
+
+static int
+yaml_parser_scan_to_next_token(yaml_parser_t *parser)
+{
+ /* Until the next token is not found. */
+
+ while (1)
+ {
+ /* Allow the BOM mark to start a line. */
+
+ if (!CACHE(parser, 1)) return 0;
+
+ if (parser->mark.column == 0 && IS_BOM(parser->buffer))
+ SKIP(parser);
+
+ /*
+ * Eat whitespaces.
+ *
+ * Tabs are allowed:
+ *
+ * - in the flow context;
+ * - in the block context, but not at the beginning of the line or
+ * after '-', '?', or ':' (complex value).
+ */
+
+ if (!CACHE(parser, 1)) return 0;
+
+ while (CHECK(parser->buffer,' ') ||
+ ((parser->flow_level || !parser->simple_key_allowed) &&
+ CHECK(parser->buffer, '\t'))) {
+ SKIP(parser);
+ if (!CACHE(parser, 1)) return 0;
+ }
+
+ /* Eat a comment until a line break. */
+
+ if (CHECK(parser->buffer, '#')) {
+ while (!IS_BREAKZ(parser->buffer)) {
+ SKIP(parser);
+ if (!CACHE(parser, 1)) return 0;
+ }
+ }
+
+ /* If it is a line break, eat it. */
+
+ if (IS_BREAK(parser->buffer))
+ {
+ if (!CACHE(parser, 2)) return 0;
+ SKIP_LINE(parser);
+
+ /* In the block context, a new line may start a simple key. */
+
+ if (!parser->flow_level) {
+ parser->simple_key_allowed = 1;
+ }
+ }
+ else
+ {
+ /* We have found a token. */
+
+ break;
+ }
+ }
+
+ return 1;
+}
+
+/*
+ * Scan a YAML-DIRECTIVE or TAG-DIRECTIVE token.
+ *
+ * Scope:
+ * %YAML 1.1 # a comment \n
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ * %TAG !yaml! tag:yaml.org,2002: \n
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ */
+
+int
+yaml_parser_scan_directive(yaml_parser_t *parser, yaml_token_t *token)
+{
+ yaml_mark_t start_mark, end_mark;
+ yaml_char_t *name = NULL;
+ int major, minor;
+ yaml_char_t *handle = NULL, *prefix = NULL;
+
+ /* Eat '%'. */
+
+ start_mark = parser->mark;
+
+ SKIP(parser);
+
+ /* Scan the directive name. */
+
+ if (!yaml_parser_scan_directive_name(parser, start_mark, &name))
+ goto error;
+
+ /* Is it a YAML directive? */
+
+ if (strcmp((char *)name, "YAML") == 0)
+ {
+ /* Scan the VERSION directive value. */
+
+ if (!yaml_parser_scan_version_directive_value(parser, start_mark,
+ &major, &minor))
+ goto error;
+
+ end_mark = parser->mark;
+
+ /* Create a VERSION-DIRECTIVE token. */
+
+ VERSION_DIRECTIVE_TOKEN_INIT(*token, major, minor,
+ start_mark, end_mark);
+ }
+
+ /* Is it a TAG directive? */
+
+ else if (strcmp((char *)name, "TAG") == 0)
+ {
+ /* Scan the TAG directive value. */
+
+ if (!yaml_parser_scan_tag_directive_value(parser, start_mark,
+ &handle, &prefix))
+ goto error;
+
+ end_mark = parser->mark;
+
+ /* Create a TAG-DIRECTIVE token. */
+
+ TAG_DIRECTIVE_TOKEN_INIT(*token, handle, prefix,
+ start_mark, end_mark);
+ }
+
+ /* Unknown directive. */
+
+ else
+ {
+ yaml_parser_set_scanner_error(parser, "while scanning a directive",
+ start_mark, "found uknown directive name");
+ goto error;
+ }
+
+ /* Eat the rest of the line including any comments. */
+
+ if (!CACHE(parser, 1)) goto error;
+
+ while (IS_BLANK(parser->buffer)) {
+ SKIP(parser);
+ if (!CACHE(parser, 1)) goto error;
+ }
+
+ if (CHECK(parser->buffer, '#')) {
+ while (!IS_BREAKZ(parser->buffer)) {
+ SKIP(parser);
+ if (!CACHE(parser, 1)) goto error;
+ }
+ }
+
+ /* Check if we are at the end of the line. */
+
+ if (!IS_BREAKZ(parser->buffer)) {
+ yaml_parser_set_scanner_error(parser, "while scanning a directive",
+ start_mark, "did not find expected comment or line break");
+ goto error;
+ }
+
+ /* Eat a line break. */
+
+ if (IS_BREAK(parser->buffer)) {
+ if (!CACHE(parser, 2)) goto error;
+ SKIP_LINE(parser);
+ }
+
+ yaml_free(name);
+
+ return 1;
+
+error:
+ yaml_free(prefix);
+ yaml_free(handle);
+ yaml_free(name);
+ return 0;
+}
+
+/*
+ * Scan the directive name.
+ *
+ * Scope:
+ * %YAML 1.1 # a comment \n
+ * ^^^^
+ * %TAG !yaml! tag:yaml.org,2002: \n
+ * ^^^
+ */
+
+static int
+yaml_parser_scan_directive_name(yaml_parser_t *parser,
+ yaml_mark_t start_mark, yaml_char_t **name)
+{
+ yaml_string_t string = NULL_STRING;
+
+ if (!STRING_INIT(parser, string, INITIAL_STRING_SIZE)) goto error;
+
+ /* Consume the directive name. */
+
+ if (!CACHE(parser, 1)) goto error;
+
+ while (IS_ALPHA(parser->buffer))
+ {
+ if (!READ(parser, string)) goto error;
+ if (!CACHE(parser, 1)) goto error;
+ }
+
+ /* Check if the name is empty. */
+
+ if (string.start == string.pointer) {
+ yaml_parser_set_scanner_error(parser, "while scanning a directive",
+ start_mark, "could not find expected directive name");
+ goto error;
+ }
+
+ /* Check for an blank character after the name. */
+
+ if (!IS_BLANKZ(parser->buffer)) {
+ yaml_parser_set_scanner_error(parser, "while scanning a directive",
+ start_mark, "found unexpected non-alphabetical character");
+ goto error;
+ }
+
+ *name = string.start;
+
+ return 1;
+
+error:
+ STRING_DEL(parser, string);
+ return 0;
+}
+
+/*
+ * Scan the value of VERSION-DIRECTIVE.
+ *
+ * Scope:
+ * %YAML 1.1 # a comment \n
+ * ^^^^^^
+ */
+
+static int
+yaml_parser_scan_version_directive_value(yaml_parser_t *parser,
+ yaml_mark_t start_mark, int *major, int *minor)
+{
+ /* Eat whitespaces. */
+
+ if (!CACHE(parser, 1)) return 0;
+
+ while (IS_BLANK(parser->buffer)) {
+ SKIP(parser);
+ if (!CACHE(parser, 1)) return 0;
+ }
+
+ /* Consume the major version number. */
+
+ if (!yaml_parser_scan_version_directive_number(parser, start_mark, major))
+ return 0;
+
+ /* Eat '.'. */
+
+ if (!CHECK(parser->buffer, '.')) {
+ return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive",
+ start_mark, "did not find expected digit or '.' character");
+ }
+
+ SKIP(parser);
+
+ /* Consume the minor version number. */
+
+ if (!yaml_parser_scan_version_directive_number(parser, start_mark, minor))
+ return 0;
+
+ return 1;
+}
+
+#define MAX_NUMBER_LENGTH 9
+
+/*
+ * Scan the version number of VERSION-DIRECTIVE.
+ *
+ * Scope:
+ * %YAML 1.1 # a comment \n
+ * ^
+ * %YAML 1.1 # a comment \n
+ * ^
+ */
+
+static int
+yaml_parser_scan_version_directive_number(yaml_parser_t *parser,
+ yaml_mark_t start_mark, int *number)
+{
+ int value = 0;
+ size_t length = 0;
+
+ /* Repeat while the next character is digit. */
+
+ if (!CACHE(parser, 1)) return 0;
+
+ while (IS_DIGIT(parser->buffer))
+ {
+ /* Check if the number is too long. */
+
+ if (++length > MAX_NUMBER_LENGTH) {
+ return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive",
+ start_mark, "found extremely long version number");
+ }
+
+ value = value*10 + AS_DIGIT(parser->buffer);
+
+ SKIP(parser);
+
+ if (!CACHE(parser, 1)) return 0;
+ }
+
+ /* Check if the number was present. */
+
+ if (!length) {
+ return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive",
+ start_mark, "did not find expected version number");
+ }
+
+ *number = value;
+
+ return 1;
+}
+
+/*
+ * Scan the value of a TAG-DIRECTIVE token.
+ *
+ * Scope:
+ * %TAG !yaml! tag:yaml.org,2002: \n
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ */
+
+static int
+yaml_parser_scan_tag_directive_value(yaml_parser_t *parser,
+ yaml_mark_t start_mark, yaml_char_t **handle, yaml_char_t **prefix)
+{
+ yaml_char_t *handle_value = NULL;
+ yaml_char_t *prefix_value = NULL;
+
+ /* Eat whitespaces. */
+
+ if (!CACHE(parser, 1)) goto error;
+
+ while (IS_BLANK(parser->buffer)) {
+ SKIP(parser);
+ if (!CACHE(parser, 1)) goto error;
+ }
+
+ /* Scan a handle. */
+
+ if (!yaml_parser_scan_tag_handle(parser, 1, start_mark, &handle_value))
+ goto error;
+
+ /* Expect a whitespace. */
+
+ if (!CACHE(parser, 1)) goto error;
+
+ if (!IS_BLANK(parser->buffer)) {
+ yaml_parser_set_scanner_error(parser, "while scanning a %TAG directive",
+ start_mark, "did not find expected whitespace");
+ goto error;
+ }
+
+ /* Eat whitespaces. */
+
+ while (IS_BLANK(parser->buffer)) {
+ SKIP(parser);
+ if (!CACHE(parser, 1)) goto error;
+ }
+
+ /* Scan a prefix. */
+
+ if (!yaml_parser_scan_tag_uri(parser, 1, NULL, start_mark, &prefix_value))
+ goto error;
+
+ /* Expect a whitespace or line break. */
+
+ if (!CACHE(parser, 1)) goto error;
+
+ if (!IS_BLANKZ(parser->buffer)) {
+ yaml_parser_set_scanner_error(parser, "while scanning a %TAG directive",
+ start_mark, "did not find expected whitespace or line break");
+ goto error;
+ }
+
+ *handle = handle_value;
+ *prefix = prefix_value;
+
+ return 1;
+
+error:
+ yaml_free(handle_value);
+ yaml_free(prefix_value);
+ return 0;
+}
+
+static int
+yaml_parser_scan_anchor(yaml_parser_t *parser, yaml_token_t *token,
+ yaml_token_type_t type)
+{
+ int length = 0;
+ yaml_mark_t start_mark, end_mark;
+ yaml_string_t string = NULL_STRING;
+
+ if (!STRING_INIT(parser, string, INITIAL_STRING_SIZE)) goto error;
+
+ /* Eat the indicator character. */
+
+ start_mark = parser->mark;
+
+ SKIP(parser);
+
+ /* Consume the value. */
+
+ if (!CACHE(parser, 1)) goto error;
+
+ while (IS_ALPHA(parser->buffer)) {
+ if (!READ(parser, string)) goto error;
+ if (!CACHE(parser, 1)) goto error;
+ length ++;
+ }
+
+ end_mark = parser->mark;
+
+ /*
+ * Check if length of the anchor is greater than 0 and it is followed by
+ * a whitespace character or one of the indicators:
+ *
+ * '?', ':', ',', ']', '}', '%', '@', '`'.
+ */
+
+ if (!length || !(IS_BLANKZ(parser->buffer) || CHECK(parser->buffer, '?')
+ || CHECK(parser->buffer, ':') || CHECK(parser->buffer, ',')
+ || CHECK(parser->buffer, ']') || CHECK(parser->buffer, '}')
+ || CHECK(parser->buffer, '%') || CHECK(parser->buffer, '@')
+ || CHECK(parser->buffer, '`'))) {
+ yaml_parser_set_scanner_error(parser, type == YAML_ANCHOR_TOKEN ?
+ "while scanning an anchor" : "while scanning an alias", start_mark,
+ "did not find expected alphabetic or numeric character");
+ goto error;
+ }
+
+ /* Create a token. */
+
+ if (type == YAML_ANCHOR_TOKEN) {
+ ANCHOR_TOKEN_INIT(*token, string.start, start_mark, end_mark);
+ }
+ else {
+ ALIAS_TOKEN_INIT(*token, string.start, start_mark, end_mark);
+ }
+
+ return 1;
+
+error:
+ STRING_DEL(parser, string);
+ return 0;
+}
+
+/*
+ * Scan a TAG token.
+ */
+
+static int
+yaml_parser_scan_tag(yaml_parser_t *parser, yaml_token_t *token)
+{
+ yaml_char_t *handle = NULL;
+ yaml_char_t *suffix = NULL;
+ yaml_mark_t start_mark, end_mark;
+
+ start_mark = parser->mark;
+
+ /* Check if the tag is in the canonical form. */
+
+ if (!CACHE(parser, 2)) goto error;
+
+ if (CHECK_AT(parser->buffer, '<', 1))
+ {
+ /* Set the handle to '' */
+
+ handle = yaml_malloc(1);
+ if (!handle) goto error;
+ handle[0] = '\0';
+
+ /* Eat '!<' */
+
+ SKIP(parser);
+ SKIP(parser);
+
+ /* Consume the tag value. */
+
+ if (!yaml_parser_scan_tag_uri(parser, 0, NULL, start_mark, &suffix))
+ goto error;
+
+ /* Check for '>' and eat it. */
+
+ if (!CHECK(parser->buffer, '>')) {
+ yaml_parser_set_scanner_error(parser, "while scanning a tag",
+ start_mark, "did not find the expected '>'");
+ goto error;
+ }
+
+ SKIP(parser);
+ }
+ else
+ {
+ /* The tag has either the '!suffix' or the '!handle!suffix' form. */
+
+ /* First, try to scan a handle. */
+
+ if (!yaml_parser_scan_tag_handle(parser, 0, start_mark, &handle))
+ goto error;
+
+ /* Check if it is, indeed, handle. */
+
+ if (handle[0] == '!' && handle[1] != '\0' && handle[strlen((char *)handle)-1] == '!')
+ {
+ /* Scan the suffix now. */
+
+ if (!yaml_parser_scan_tag_uri(parser, 0, NULL, start_mark, &suffix))
+ goto error;
+ }
+ else
+ {
+ /* It wasn't a handle after all. Scan the rest of the tag. */
+
+ if (!yaml_parser_scan_tag_uri(parser, 0, handle, start_mark, &suffix))
+ goto error;
+
+ /* Set the handle to '!'. */
+
+ yaml_free(handle);
+ handle = yaml_malloc(2);
+ if (!handle) goto error;
+ handle[0] = '!';
+ handle[1] = '\0';
+
+ /*
+ * A special case: the '!' tag. Set the handle to '' and the
+ * suffix to '!'.
+ */
+
+ if (suffix[0] == '\0') {
+ yaml_char_t *tmp = handle;
+ handle = suffix;
+ suffix = tmp;
+ }
+ }
+ }
+
+ /* Check the character which ends the tag. */
+
+ if (!CACHE(parser, 1)) goto error;
+
+ if (!IS_BLANKZ(parser->buffer)) {
+ yaml_parser_set_scanner_error(parser, "while scanning a tag",
+ start_mark, "did not find expected whitespace or line break");
+ goto error;
+ }
+
+ end_mark = parser->mark;
+
+ /* Create a token. */
+
+ TAG_TOKEN_INIT(*token, handle, suffix, start_mark, end_mark);
+
+ return 1;
+
+error:
+ yaml_free(handle);
+ yaml_free(suffix);
+ return 0;
+}
+
+/*
+ * Scan a tag handle.
+ */
+
+static int
+yaml_parser_scan_tag_handle(yaml_parser_t *parser, int directive,
+ yaml_mark_t start_mark, yaml_char_t **handle)
+{
+ yaml_string_t string = NULL_STRING;
+
+ if (!STRING_INIT(parser, string, INITIAL_STRING_SIZE)) goto error;
+
+ /* Check the initial '!' character. */
+
+ if (!CACHE(parser, 1)) goto error;
+
+ if (!CHECK(parser->buffer, '!')) {
+ yaml_parser_set_scanner_error(parser, directive ?
+ "while scanning a tag directive" : "while scanning a tag",
+ start_mark, "did not find expected '!'");
+ goto error;
+ }
+
+ /* Copy the '!' character. */
+
+ if (!READ(parser, string)) goto error;
+
+ /* Copy all subsequent alphabetical and numerical characters. */
+
+ if (!CACHE(parser, 1)) goto error;
+
+ while (IS_ALPHA(parser->buffer))
+ {
+ if (!READ(parser, string)) goto error;
+ if (!CACHE(parser, 1)) goto error;
+ }
+
+ /* Check if the trailing character is '!' and copy it. */
+
+ if (CHECK(parser->buffer, '!'))
+ {
+ if (!READ(parser, string)) goto error;
+ }
+ else
+ {
+ /*
+ * It's either the '!' tag or not really a tag handle. If it's a %TAG
+ * directive, it's an error. If it's a tag token, it must be a part of
+ * URI.
+ */
+
+ if (directive && !(string.start[0] == '!' && string.start[1] == '\0')) {
+ yaml_parser_set_scanner_error(parser, "while parsing a tag directive",
+ start_mark, "did not find expected '!'");
+ goto error;
+ }
+ }
+
+ *handle = string.start;
+
+ return 1;
+
+error:
+ STRING_DEL(parser, string);
+ return 0;
+}
+
+/*
+ * Scan a tag.
+ */
+
+static int
+yaml_parser_scan_tag_uri(yaml_parser_t *parser, int directive,
+ yaml_char_t *head, yaml_mark_t start_mark, yaml_char_t **uri)
+{
+ size_t length = head ? strlen((char *)head) : 0;
+ yaml_string_t string = NULL_STRING;
+
+ if (!STRING_INIT(parser, string, INITIAL_STRING_SIZE)) goto error;
+
+ /* Resize the string to include the head. */
+
+ while ((size_t)(string.end - string.start) <= length) {
+ if (!yaml_string_extend(&string.start, &string.pointer, &string.end)) {
+ parser->error = YAML_MEMORY_ERROR;
+ goto error;
+ }
+ }
+
+ /*
+ * Copy the head if needed.
+ *
+ * Note that we don't copy the leading '!' character.
+ */
+
+ if (length > 1) {
+ memcpy(string.start, head+1, length-1);
+ string.pointer += length-1;
+ }
+
+ /* Scan the tag. */
+
+ if (!CACHE(parser, 1)) goto error;
+
+ /*
+ * The set of characters that may appear in URI is as follows:
+ *
+ * '0'-'9', 'A'-'Z', 'a'-'z', '_', '-', ';', '/', '?', ':', '@', '&',
+ * '=', '+', '$', ',', '.', '!', '~', '*', '\'', '(', ')', '[', ']',
+ * '%'.
+ */
+
+ while (IS_ALPHA(parser->buffer) || CHECK(parser->buffer, ';')
+ || CHECK(parser->buffer, '/') || CHECK(parser->buffer, '?')
+ || CHECK(parser->buffer, ':') || CHECK(parser->buffer, '@')
+ || CHECK(parser->buffer, '&') || CHECK(parser->buffer, '=')
+ || CHECK(parser->buffer, '+') || CHECK(parser->buffer, '$')
+ || CHECK(parser->buffer, ',') || CHECK(parser->buffer, '.')
+ || CHECK(parser->buffer, '!') || CHECK(parser->buffer, '~')
+ || CHECK(parser->buffer, '*') || CHECK(parser->buffer, '\'')
+ || CHECK(parser->buffer, '(') || CHECK(parser->buffer, ')')
+ || CHECK(parser->buffer, '[') || CHECK(parser->buffer, ']')
+ || CHECK(parser->buffer, '%'))
+ {
+ /* Check if it is a URI-escape sequence. */
+
+ if (CHECK(parser->buffer, '%')) {
+ if (!STRING_EXTEND(parser, string))
+ goto error;
+
+ if (!yaml_parser_scan_uri_escapes(parser,
+ directive, start_mark, &string)) goto error;
+ }
+ else {
+ if (!READ(parser, string)) goto error;
+ }
+
+ length ++;
+ if (!CACHE(parser, 1)) goto error;
+ }
+
+ /* Check if the tag is non-empty. */
+
+ if (!length) {
+ if (!STRING_EXTEND(parser, string))
+ goto error;
+
+ yaml_parser_set_scanner_error(parser, directive ?
+ "while parsing a %TAG directive" : "while parsing a tag",
+ start_mark, "did not find expected tag URI");
+ goto error;
+ }
+
+ *uri = string.start;
+
+ return 1;
+
+error:
+ STRING_DEL(parser, string);
+ return 0;
+}
+
+/*
+ * Decode an URI-escape sequence corresponding to a single UTF-8 character.
+ */
+
+static int
+yaml_parser_scan_uri_escapes(yaml_parser_t *parser, int directive,
+ yaml_mark_t start_mark, yaml_string_t *string)
+{
+ int width = 0;
+
+ /* Decode the required number of characters. */
+
+ do {
+
+ unsigned char octet = 0;
+
+ /* Check for a URI-escaped octet. */
+
+ if (!CACHE(parser, 3)) return 0;
+
+ if (!(CHECK(parser->buffer, '%')
+ && IS_HEX_AT(parser->buffer, 1)
+ && IS_HEX_AT(parser->buffer, 2))) {
+ return yaml_parser_set_scanner_error(parser, directive ?
+ "while parsing a %TAG directive" : "while parsing a tag",
+ start_mark, "did not find URI escaped octet");
+ }
+
+ /* Get the octet. */
+
+ octet = (AS_HEX_AT(parser->buffer, 1) << 4) + AS_HEX_AT(parser->buffer, 2);
+
+ /* If it is the leading octet, determine the length of the UTF-8 sequence. */
+
+ if (!width)
+ {
+ width = (octet & 0x80) == 0x00 ? 1 :
+ (octet & 0xE0) == 0xC0 ? 2 :
+ (octet & 0xF0) == 0xE0 ? 3 :
+ (octet & 0xF8) == 0xF0 ? 4 : 0;
+ if (!width) {
+ return yaml_parser_set_scanner_error(parser, directive ?
+ "while parsing a %TAG directive" : "while parsing a tag",
+ start_mark, "found an incorrect leading UTF-8 octet");
+ }
+ }
+ else
+ {
+ /* Check if the trailing octet is correct. */
+
+ if ((octet & 0xC0) != 0x80) {
+ return yaml_parser_set_scanner_error(parser, directive ?
+ "while parsing a %TAG directive" : "while parsing a tag",
+ start_mark, "found an incorrect trailing UTF-8 octet");
+ }
+ }
+
+ /* Copy the octet and move the pointers. */
+
+ *(string->pointer++) = octet;
+ SKIP(parser);
+ SKIP(parser);
+ SKIP(parser);
+
+ } while (--width);
+
+ return 1;
+}
+
+/*
+ * Scan a block scalar.
+ */
+
+static int
+yaml_parser_scan_block_scalar(yaml_parser_t *parser, yaml_token_t *token,
+ int literal)
+{
+ yaml_mark_t start_mark;
+ yaml_mark_t end_mark;
+ yaml_string_t string = NULL_STRING;
+ yaml_string_t leading_break = NULL_STRING;
+ yaml_string_t trailing_breaks = NULL_STRING;
+ int chomping = 0;
+ int increment = 0;
+ int indent = 0;
+ int leading_blank = 0;
+ int trailing_blank = 0;
+
+ if (!STRING_INIT(parser, string, INITIAL_STRING_SIZE)) goto error;
+ if (!STRING_INIT(parser, leading_break, INITIAL_STRING_SIZE)) goto error;
+ if (!STRING_INIT(parser, trailing_breaks, INITIAL_STRING_SIZE)) goto error;
+
+ /* Eat the indicator '|' or '>'. */
+
+ start_mark = parser->mark;
+
+ SKIP(parser);
+
+ /* Scan the additional block scalar indicators. */
+
+ if (!CACHE(parser, 1)) goto error;
+
+ /* Check for a chomping indicator. */
+
+ if (CHECK(parser->buffer, '+') || CHECK(parser->buffer, '-'))
+ {
+ /* Set the chomping method and eat the indicator. */
+
+ chomping = CHECK(parser->buffer, '+') ? +1 : -1;
+
+ SKIP(parser);
+
+ /* Check for an indentation indicator. */
+
+ if (!CACHE(parser, 1)) goto error;
+
+ if (IS_DIGIT(parser->buffer))
+ {
+ /* Check that the intendation is greater than 0. */
+
+ if (CHECK(parser->buffer, '0')) {
+ yaml_parser_set_scanner_error(parser, "while scanning a block scalar",
+ start_mark, "found an intendation indicator equal to 0");
+ goto error;
+ }
+
+ /* Get the intendation level and eat the indicator. */
+
+ increment = AS_DIGIT(parser->buffer);
+
+ SKIP(parser);
+ }
+ }
+
+ /* Do the same as above, but in the opposite order. */
+
+ else if (IS_DIGIT(parser->buffer))
+ {
+ if (CHECK(parser->buffer, '0')) {
+ yaml_parser_set_scanner_error(parser, "while scanning a block scalar",
+ start_mark, "found an intendation indicator equal to 0");
+ goto error;
+ }
+
+ increment = AS_DIGIT(parser->buffer);
+
+ SKIP(parser);
+
+ if (!CACHE(parser, 1)) goto error;
+
+ if (CHECK(parser->buffer, '+') || CHECK(parser->buffer, '-')) {
+ chomping = CHECK(parser->buffer, '+') ? +1 : -1;
+
+ SKIP(parser);
+ }
+ }
+
+ /* Eat whitespaces and comments to the end of the line. */
+
+ if (!CACHE(parser, 1)) goto error;
+
+ while (IS_BLANK(parser->buffer)) {
+ SKIP(parser);
+ if (!CACHE(parser, 1)) goto error;
+ }
+
+ if (CHECK(parser->buffer, '#')) {
+ while (!IS_BREAKZ(parser->buffer)) {
+ SKIP(parser);
+ if (!CACHE(parser, 1)) goto error;
+ }
+ }
+
+ /* Check if we are at the end of the line. */
+
+ if (!IS_BREAKZ(parser->buffer)) {
+ yaml_parser_set_scanner_error(parser, "while scanning a block scalar",
+ start_mark, "did not find expected comment or line break");
+ goto error;
+ }
+
+ /* Eat a line break. */
+
+ if (IS_BREAK(parser->buffer)) {
+ if (!CACHE(parser, 2)) goto error;
+ SKIP_LINE(parser);
+ }
+
+ end_mark = parser->mark;
+
+ /* Set the intendation level if it was specified. */
+
+ if (increment) {
+ indent = parser->indent >= 0 ? parser->indent+increment : increment;
+ }
+
+ /* Scan the leading line breaks and determine the indentation level if needed. */
+
+ if (!yaml_parser_scan_block_scalar_breaks(parser, &indent, &trailing_breaks,
+ start_mark, &end_mark)) goto error;
+
+ /* Scan the block scalar content. */
+
+ if (!CACHE(parser, 1)) goto error;
+
+ while ((int)parser->mark.column == indent && !IS_Z(parser->buffer))
+ {
+ /*
+ * We are at the beginning of a non-empty line.
+ */
+
+ /* Is it a trailing whitespace? */
+
+ trailing_blank = IS_BLANK(parser->buffer);
+
+ /* Check if we need to fold the leading line break. */
+
+ if (!literal && (*leading_break.start == '\n')
+ && !leading_blank && !trailing_blank)
+ {
+ /* Do we need to join the lines by space? */
+
+ if (*trailing_breaks.start == '\0') {
+ if (!STRING_EXTEND(parser, string)) goto error;
+ *(string.pointer ++) = ' ';
+ }
+
+ CLEAR(parser, leading_break);
+ }
+ else {
+ if (!JOIN(parser, string, leading_break)) goto error;
+ CLEAR(parser, leading_break);
+ }
+
+ /* Append the remaining line breaks. */
+
+ if (!JOIN(parser, string, trailing_breaks)) goto error;
+ CLEAR(parser, trailing_breaks);
+
+ /* Is it a leading whitespace? */
+
+ leading_blank = IS_BLANK(parser->buffer);
+
+ /* Consume the current line. */
+
+ while (!IS_BREAKZ(parser->buffer)) {
+ if (!READ(parser, string)) goto error;
+ if (!CACHE(parser, 1)) goto error;
+ }
+
+ /* Consume the line break. */
+
+ if (!CACHE(parser, 2)) goto error;
+
+ if (!READ_LINE(parser, leading_break)) goto error;
+
+ /* Eat the following intendation spaces and line breaks. */
+
+ if (!yaml_parser_scan_block_scalar_breaks(parser,
+ &indent, &trailing_breaks, start_mark, &end_mark)) goto error;
+ }
+
+ /* Chomp the tail. */
+
+ if (chomping != -1) {
+ if (!JOIN(parser, string, leading_break)) goto error;
+ }
+ if (chomping == 1) {
+ if (!JOIN(parser, string, trailing_breaks)) goto error;
+ }
+
+ /* Create a token. */
+
+ SCALAR_TOKEN_INIT(*token, string.start, string.pointer-string.start,
+ literal ? YAML_LITERAL_SCALAR_STYLE : YAML_FOLDED_SCALAR_STYLE,
+ start_mark, end_mark);
+
+ STRING_DEL(parser, leading_break);
+ STRING_DEL(parser, trailing_breaks);
+
+ return 1;
+
+error:
+ STRING_DEL(parser, string);
+ STRING_DEL(parser, leading_break);
+ STRING_DEL(parser, trailing_breaks);
+
+ return 0;
+}
+
+/*
+ * Scan intendation spaces and line breaks for a block scalar. Determine the
+ * intendation level if needed.
+ */
+
+static int
+yaml_parser_scan_block_scalar_breaks(yaml_parser_t *parser,
+ int *indent, yaml_string_t *breaks,
+ yaml_mark_t start_mark, yaml_mark_t *end_mark)
+{
+ int max_indent = 0;
+
+ *end_mark = parser->mark;
+
+ /* Eat the intendation spaces and line breaks. */
+
+ while (1)
+ {
+ /* Eat the intendation spaces. */
+
+ if (!CACHE(parser, 1)) return 0;
+
+ while ((!*indent || (int)parser->mark.column < *indent)
+ && IS_SPACE(parser->buffer)) {
+ SKIP(parser);
+ if (!CACHE(parser, 1)) return 0;
+ }
+
+ if ((int)parser->mark.column > max_indent)
+ max_indent = (int)parser->mark.column;
+
+ /* Check for a tab character messing the intendation. */
+
+ if ((!*indent || (int)parser->mark.column < *indent)
+ && IS_TAB(parser->buffer)) {
+ return yaml_parser_set_scanner_error(parser, "while scanning a block scalar",
+ start_mark, "found a tab character where an intendation space is expected");
+ }
+
+ /* Have we found a non-empty line? */
+
+ if (!IS_BREAK(parser->buffer)) break;
+
+ /* Consume the line break. */
+
+ if (!CACHE(parser, 2)) return 0;
+ if (!READ_LINE(parser, *breaks)) return 0;
+ *end_mark = parser->mark;
+ }
+
+ /* Determine the indentation level if needed. */
+
+ if (!*indent) {
+ *indent = max_indent;
+ if (*indent < parser->indent + 1)
+ *indent = parser->indent + 1;
+ if (*indent < 1)
+ *indent = 1;
+ }
+
+ return 1;
+}
+
+/*
+ * Scan a quoted scalar.
+ */
+
+static int
+yaml_parser_scan_flow_scalar(yaml_parser_t *parser, yaml_token_t *token,
+ int single)
+{
+ yaml_mark_t start_mark;
+ yaml_mark_t end_mark;
+ yaml_string_t string = NULL_STRING;
+ yaml_string_t leading_break = NULL_STRING;
+ yaml_string_t trailing_breaks = NULL_STRING;
+ yaml_string_t whitespaces = NULL_STRING;
+ int leading_blanks;
+
+ if (!STRING_INIT(parser, string, INITIAL_STRING_SIZE)) goto error;
+ if (!STRING_INIT(parser, leading_break, INITIAL_STRING_SIZE)) goto error;
+ if (!STRING_INIT(parser, trailing_breaks, INITIAL_STRING_SIZE)) goto error;
+ if (!STRING_INIT(parser, whitespaces, INITIAL_STRING_SIZE)) goto error;
+
+ /* Eat the left quote. */
+
+ start_mark = parser->mark;
+
+ SKIP(parser);
+
+ /* Consume the content of the quoted scalar. */
+
+ while (1)
+ {
+ /* Check that there are no document indicators at the beginning of the line. */
+
+ if (!CACHE(parser, 4)) goto error;
+
+ if (parser->mark.column == 0 &&
+ ((CHECK_AT(parser->buffer, '-', 0) &&
+ CHECK_AT(parser->buffer, '-', 1) &&
+ CHECK_AT(parser->buffer, '-', 2)) ||
+ (CHECK_AT(parser->buffer, '.', 0) &&
+ CHECK_AT(parser->buffer, '.', 1) &&
+ CHECK_AT(parser->buffer, '.', 2))) &&
+ IS_BLANKZ_AT(parser->buffer, 3))
+ {
+ yaml_parser_set_scanner_error(parser, "while scanning a quoted scalar",
+ start_mark, "found unexpected document indicator");
+ goto error;
+ }
+
+ /* Check for EOF. */
+
+ if (IS_Z(parser->buffer)) {
+ yaml_parser_set_scanner_error(parser, "while scanning a quoted scalar",
+ start_mark, "found unexpected end of stream");
+ goto error;
+ }
+
+ /* Consume non-blank characters. */
+
+ if (!CACHE(parser, 2)) goto error;
+
+ leading_blanks = 0;
+
+ while (!IS_BLANKZ(parser->buffer))
+ {
+ /* Check for an escaped single quote. */
+
+ if (single && CHECK_AT(parser->buffer, '\'', 0)
+ && CHECK_AT(parser->buffer, '\'', 1))
+ {
+ if (!STRING_EXTEND(parser, string)) goto error;
+ *(string.pointer++) = '\'';
+ SKIP(parser);
+ SKIP(parser);
+ }
+
+ /* Check for the right quote. */
+
+ else if (CHECK(parser->buffer, single ? '\'' : '"'))
+ {
+ break;
+ }
+
+ /* Check for an escaped line break. */
+
+ else if (!single && CHECK(parser->buffer, '\\')
+ && IS_BREAK_AT(parser->buffer, 1))
+ {
+ if (!CACHE(parser, 3)) goto error;
+ SKIP(parser);
+ SKIP_LINE(parser);
+ leading_blanks = 1;
+ break;
+ }
+
+ /* Check for an escape sequence. */
+
+ else if (!single && CHECK(parser->buffer, '\\'))
+ {
+ size_t code_length = 0;
+
+ if (!STRING_EXTEND(parser, string)) goto error;
+
+ /* Check the escape character. */
+
+ switch (parser->buffer.pointer[1])
+ {
+ case '0':
+ *(string.pointer++) = '\0';
+ break;
+
+ case 'a':
+ *(string.pointer++) = '\x07';
+ break;
+
+ case 'b':
+ *(string.pointer++) = '\x08';
+ break;
+
+ case 't':
+ case '\t':
+ *(string.pointer++) = '\x09';
+ break;
+
+ case 'n':
+ *(string.pointer++) = '\x0A';
+ break;
+
+ case 'v':
+ *(string.pointer++) = '\x0B';
+ break;
+
+ case 'f':
+ *(string.pointer++) = '\x0C';
+ break;
+
+ case 'r':
+ *(string.pointer++) = '\x0D';
+ break;
+
+ case 'e':
+ *(string.pointer++) = '\x1B';
+ break;
+
+ case ' ':
+ *(string.pointer++) = '\x20';
+ break;
+
+ case '"':
+ *(string.pointer++) = '"';
+ break;
+
+ case '\'':
+ *(string.pointer++) = '\'';
+ break;
+
+ case '\\':
+ *(string.pointer++) = '\\';
+ break;
+
+ case 'N': /* NEL (#x85) */
+ *(string.pointer++) = '\xC2';
+ *(string.pointer++) = '\x85';
+ break;
+
+ case '_': /* #xA0 */
+ *(string.pointer++) = '\xC2';
+ *(string.pointer++) = '\xA0';
+ break;
+
+ case 'L': /* LS (#x2028) */
+ *(string.pointer++) = '\xE2';
+ *(string.pointer++) = '\x80';
+ *(string.pointer++) = '\xA8';
+ break;
+
+ case 'P': /* PS (#x2029) */
+ *(string.pointer++) = '\xE2';
+ *(string.pointer++) = '\x80';
+ *(string.pointer++) = '\xA9';
+ break;
+
+ case 'x':
+ code_length = 2;
+ break;
+
+ case 'u':
+ code_length = 4;
+ break;
+
+ case 'U':
+ code_length = 8;
+ break;
+
+ default:
+ yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar",
+ start_mark, "found unknown escape character");
+ goto error;
+ }
+
+ SKIP(parser);
+ SKIP(parser);
+
+ /* Consume an arbitrary escape code. */
+
+ if (code_length)
+ {
+ unsigned int value = 0;
+ size_t k;
+
+ /* Scan the character value. */
+
+ if (!CACHE(parser, code_length)) goto error;
+
+ for (k = 0; k < code_length; k ++) {
+ if (!IS_HEX_AT(parser->buffer, k)) {
+ yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar",
+ start_mark, "did not find expected hexdecimal number");
+ goto error;
+ }
+ value = (value << 4) + AS_HEX_AT(parser->buffer, k);
+ }
+
+ /* Check the value and write the character. */
+
+ if ((value >= 0xD800 && value <= 0xDFFF) || value > 0x10FFFF) {
+ yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar",
+ start_mark, "found invalid Unicode character escape code");
+ goto error;
+ }
+
+ if (value <= 0x7F) {
+ *(string.pointer++) = value;
+ }
+ else if (value <= 0x7FF) {
+ *(string.pointer++) = 0xC0 + (value >> 6);
+ *(string.pointer++) = 0x80 + (value & 0x3F);
+ }
+ else if (value <= 0xFFFF) {
+ *(string.pointer++) = 0xE0 + (value >> 12);
+ *(string.pointer++) = 0x80 + ((value >> 6) & 0x3F);
+ *(string.pointer++) = 0x80 + (value & 0x3F);
+ }
+ else {
+ *(string.pointer++) = 0xF0 + (value >> 18);
+ *(string.pointer++) = 0x80 + ((value >> 12) & 0x3F);
+ *(string.pointer++) = 0x80 + ((value >> 6) & 0x3F);
+ *(string.pointer++) = 0x80 + (value & 0x3F);
+ }
+
+ /* Advance the pointer. */
+
+ for (k = 0; k < code_length; k ++) {
+ SKIP(parser);
+ }
+ }
+ }
+
+ else
+ {
+ /* It is a non-escaped non-blank character. */
+
+ if (!READ(parser, string)) goto error;
+ }
+
+ if (!CACHE(parser, 2)) goto error;
+ }
+
+ /* Check if we are at the end of the scalar. */
+
+ if (CHECK(parser->buffer, single ? '\'' : '"'))
+ break;
+
+ /* Consume blank characters. */
+
+ if (!CACHE(parser, 1)) goto error;
+
+ while (IS_BLANK(parser->buffer) || IS_BREAK(parser->buffer))
+ {
+ if (IS_BLANK(parser->buffer))
+ {
+ /* Consume a space or a tab character. */
+
+ if (!leading_blanks) {
+ if (!READ(parser, whitespaces)) goto error;
+ }
+ else {
+ SKIP(parser);
+ }
+ }
+ else
+ {
+ if (!CACHE(parser, 2)) goto error;
+
+ /* Check if it is a first line break. */
+
+ if (!leading_blanks)
+ {
+ CLEAR(parser, whitespaces);
+ if (!READ_LINE(parser, leading_break)) goto error;
+ leading_blanks = 1;
+ }
+ else
+ {
+ if (!READ_LINE(parser, trailing_breaks)) goto error;
+ }
+ }
+ if (!CACHE(parser, 1)) goto error;
+ }
+
+ /* Join the whitespaces or fold line breaks. */
+
+ if (leading_blanks)
+ {
+ /* Do we need to fold line breaks? */
+
+ if (leading_break.start[0] == '\n') {
+ if (trailing_breaks.start[0] == '\0') {
+ if (!STRING_EXTEND(parser, string)) goto error;
+ *(string.pointer++) = ' ';
+ }
+ else {
+ if (!JOIN(parser, string, trailing_breaks)) goto error;
+ CLEAR(parser, trailing_breaks);
+ }
+ CLEAR(parser, leading_break);
+ }
+ else {
+ if (!JOIN(parser, string, leading_break)) goto error;
+ if (!JOIN(parser, string, trailing_breaks)) goto error;
+ CLEAR(parser, leading_break);
+ CLEAR(parser, trailing_breaks);
+ }
+ }
+ else
+ {
+ if (!JOIN(parser, string, whitespaces)) goto error;
+ CLEAR(parser, whitespaces);
+ }
+ }
+
+ /* Eat the right quote. */
+
+ SKIP(parser);
+
+ end_mark = parser->mark;
+
+ /* Create a token. */
+
+ SCALAR_TOKEN_INIT(*token, string.start, string.pointer-string.start,
+ single ? YAML_SINGLE_QUOTED_SCALAR_STYLE : YAML_DOUBLE_QUOTED_SCALAR_STYLE,
+ start_mark, end_mark);
+
+ STRING_DEL(parser, leading_break);
+ STRING_DEL(parser, trailing_breaks);
+ STRING_DEL(parser, whitespaces);
+
+ return 1;
+
+error:
+ STRING_DEL(parser, string);
+ STRING_DEL(parser, leading_break);
+ STRING_DEL(parser, trailing_breaks);
+ STRING_DEL(parser, whitespaces);
+
+ return 0;
+}
+
+/*
+ * Scan a plain scalar.
+ */
+
+static int
+yaml_parser_scan_plain_scalar(yaml_parser_t *parser, yaml_token_t *token)
+{
+ yaml_mark_t start_mark;
+ yaml_mark_t end_mark;
+ yaml_string_t string = NULL_STRING;
+ yaml_string_t leading_break = NULL_STRING;
+ yaml_string_t trailing_breaks = NULL_STRING;
+ yaml_string_t whitespaces = NULL_STRING;
+ int leading_blanks = 0;
+ int indent = parser->indent+1;
+
+ if (!STRING_INIT(parser, string, INITIAL_STRING_SIZE)) goto error;
+ if (!STRING_INIT(parser, leading_break, INITIAL_STRING_SIZE)) goto error;
+ if (!STRING_INIT(parser, trailing_breaks, INITIAL_STRING_SIZE)) goto error;
+ if (!STRING_INIT(parser, whitespaces, INITIAL_STRING_SIZE)) goto error;
+
+ start_mark = end_mark = parser->mark;
+
+ /* Consume the content of the plain scalar. */
+
+ while (1)
+ {
+ /* Check for a document indicator. */
+
+ if (!CACHE(parser, 4)) goto error;
+
+ if (parser->mark.column == 0 &&
+ ((CHECK_AT(parser->buffer, '-', 0) &&
+ CHECK_AT(parser->buffer, '-', 1) &&
+ CHECK_AT(parser->buffer, '-', 2)) ||
+ (CHECK_AT(parser->buffer, '.', 0) &&
+ CHECK_AT(parser->buffer, '.', 1) &&
+ CHECK_AT(parser->buffer, '.', 2))) &&
+ IS_BLANKZ_AT(parser->buffer, 3)) break;
+
+ /* Check for a comment. */
+
+ if (CHECK(parser->buffer, '#'))
+ break;
+
+ /* Consume non-blank characters. */
+
+ while (!IS_BLANKZ(parser->buffer))
+ {
+ /* Check for 'x:x' in the flow context. TODO: Fix the test "spec-08-13". */
+
+ if (parser->flow_level
+ && CHECK(parser->buffer, ':')
+ && !IS_BLANKZ_AT(parser->buffer, 1)) {
+ yaml_parser_set_scanner_error(parser, "while scanning a plain scalar",
+ start_mark, "found unexpected ':'");
+ goto error;
+ }
+
+ /* Check for indicators that may end a plain scalar. */
+
+ if ((CHECK(parser->buffer, ':') && IS_BLANKZ_AT(parser->buffer, 1))
+ || (parser->flow_level &&
+ (CHECK(parser->buffer, ',') || CHECK(parser->buffer, ':')
+ || CHECK(parser->buffer, '?') || CHECK(parser->buffer, '[')
+ || CHECK(parser->buffer, ']') || CHECK(parser->buffer, '{')
+ || CHECK(parser->buffer, '}'))))
+ break;
+
+ /* Check if we need to join whitespaces and breaks. */
+
+ if (leading_blanks || whitespaces.start != whitespaces.pointer)
+ {
+ if (leading_blanks)
+ {
+ /* Do we need to fold line breaks? */
+
+ if (leading_break.start[0] == '\n') {
+ if (trailing_breaks.start[0] == '\0') {
+ if (!STRING_EXTEND(parser, string)) goto error;
+ *(string.pointer++) = ' ';
+ }
+ else {
+ if (!JOIN(parser, string, trailing_breaks)) goto error;
+ CLEAR(parser, trailing_breaks);
+ }
+ CLEAR(parser, leading_break);
+ }
+ else {
+ if (!JOIN(parser, string, leading_break)) goto error;
+ if (!JOIN(parser, string, trailing_breaks)) goto error;
+ CLEAR(parser, leading_break);
+ CLEAR(parser, trailing_breaks);
+ }
+
+ leading_blanks = 0;
+ }
+ else
+ {
+ if (!JOIN(parser, string, whitespaces)) goto error;
+ CLEAR(parser, whitespaces);
+ }
+ }
+
+ /* Copy the character. */
+
+ if (!READ(parser, string)) goto error;
+
+ end_mark = parser->mark;
+
+ if (!CACHE(parser, 2)) goto error;
+ }
+
+ /* Is it the end? */
+
+ if (!(IS_BLANK(parser->buffer) || IS_BREAK(parser->buffer)))
+ break;
+
+ /* Consume blank characters. */
+
+ if (!CACHE(parser, 1)) goto error;
+
+ while (IS_BLANK(parser->buffer) || IS_BREAK(parser->buffer))
+ {
+ if (IS_BLANK(parser->buffer))
+ {
+ /* Check for tab character that abuse intendation. */
+
+ if (leading_blanks && (int)parser->mark.column < indent
+ && IS_TAB(parser->buffer)) {
+ yaml_parser_set_scanner_error(parser, "while scanning a plain scalar",
+ start_mark, "found a tab character that violate intendation");
+ goto error;
+ }
+
+ /* Consume a space or a tab character. */
+
+ if (!leading_blanks) {
+ if (!READ(parser, whitespaces)) goto error;
+ }
+ else {
+ SKIP(parser);
+ }
+ }
+ else
+ {
+ if (!CACHE(parser, 2)) goto error;
+
+ /* Check if it is a first line break. */
+
+ if (!leading_blanks)
+ {
+ CLEAR(parser, whitespaces);
+ if (!READ_LINE(parser, leading_break)) goto error;
+ leading_blanks = 1;
+ }
+ else
+ {
+ if (!READ_LINE(parser, trailing_breaks)) goto error;
+ }
+ }
+ if (!CACHE(parser, 1)) goto error;
+ }
+
+ /* Check intendation level. */
+
+ if (!parser->flow_level && (int)parser->mark.column < indent)
+ break;
+ }
+
+ /* Create a token. */
+
+ SCALAR_TOKEN_INIT(*token, string.start, string.pointer-string.start,
+ YAML_PLAIN_SCALAR_STYLE, start_mark, end_mark);
+
+ /* Note that we change the 'simple_key_allowed' flag. */
+
+ if (leading_blanks) {
+ parser->simple_key_allowed = 1;
+ }
+
+ STRING_DEL(parser, leading_break);
+ STRING_DEL(parser, trailing_breaks);
+ STRING_DEL(parser, whitespaces);
+
+ return 1;
+
+error:
+ STRING_DEL(parser, string);
+ STRING_DEL(parser, leading_break);
+ STRING_DEL(parser, trailing_breaks);
+ STRING_DEL(parser, whitespaces);
+
+ return 0;
+}
+
diff --git a/ext/psych/yaml/writer.c b/ext/psych/yaml/writer.c
new file mode 100644
index 0000000..5d57f39
--- /dev/null
+++ b/ext/psych/yaml/writer.c
@@ -0,0 +1,141 @@
+
+#include "yaml_private.h"
+
+/*
+ * Declarations.
+ */
+
+static int
+yaml_emitter_set_writer_error(yaml_emitter_t *emitter, const char *problem);
+
+YAML_DECLARE(int)
+yaml_emitter_flush(yaml_emitter_t *emitter);
+
+/*
+ * Set the writer error and return 0.
+ */
+
+static int
+yaml_emitter_set_writer_error(yaml_emitter_t *emitter, const char *problem)
+{
+ emitter->error = YAML_WRITER_ERROR;
+ emitter->problem = problem;
+
+ return 0;
+}
+
+/*
+ * Flush the output buffer.
+ */
+
+YAML_DECLARE(int)
+yaml_emitter_flush(yaml_emitter_t *emitter)
+{
+ int low, high;
+
+ assert(emitter); /* Non-NULL emitter object is expected. */
+ assert(emitter->write_handler); /* Write handler must be set. */
+ assert(emitter->encoding); /* Output encoding must be set. */
+
+ emitter->buffer.last = emitter->buffer.pointer;
+ emitter->buffer.pointer = emitter->buffer.start;
+
+ /* Check if the buffer is empty. */
+
+ if (emitter->buffer.start == emitter->buffer.last) {
+ return 1;
+ }
+
+ /* If the output encoding is UTF-8, we don't need to recode the buffer. */
+
+ if (emitter->encoding == YAML_UTF8_ENCODING)
+ {
+ if (emitter->write_handler(emitter->write_handler_data,
+ emitter->buffer.start,
+ emitter->buffer.last - emitter->buffer.start)) {
+ emitter->buffer.last = emitter->buffer.start;
+ emitter->buffer.pointer = emitter->buffer.start;
+ return 1;
+ }
+ else {
+ return yaml_emitter_set_writer_error(emitter, "write error");
+ }
+ }
+
+ /* Recode the buffer into the raw buffer. */
+
+ low = (emitter->encoding == YAML_UTF16LE_ENCODING ? 0 : 1);
+ high = (emitter->encoding == YAML_UTF16LE_ENCODING ? 1 : 0);
+
+ while (emitter->buffer.pointer != emitter->buffer.last)
+ {
+ unsigned char octet;
+ unsigned int width;
+ unsigned int value;
+ size_t k;
+
+ /*
+ * See the "reader.c" code for more details on UTF-8 encoding. Note
+ * that we assume that the buffer contains a valid UTF-8 sequence.
+ */
+
+ /* Read the next UTF-8 character. */
+
+ octet = emitter->buffer.pointer[0];
+
+ width = (octet & 0x80) == 0x00 ? 1 :
+ (octet & 0xE0) == 0xC0 ? 2 :
+ (octet & 0xF0) == 0xE0 ? 3 :
+ (octet & 0xF8) == 0xF0 ? 4 : 0;
+
+ value = (octet & 0x80) == 0x00 ? octet & 0x7F :
+ (octet & 0xE0) == 0xC0 ? octet & 0x1F :
+ (octet & 0xF0) == 0xE0 ? octet & 0x0F :
+ (octet & 0xF8) == 0xF0 ? octet & 0x07 : 0;
+
+ for (k = 1; k < width; k ++) {
+ octet = emitter->buffer.pointer[k];
+ value = (value << 6) + (octet & 0x3F);
+ }
+
+ emitter->buffer.pointer += width;
+
+ /* Write the character. */
+
+ if (value < 0x10000)
+ {
+ emitter->raw_buffer.last[high] = value >> 8;
+ emitter->raw_buffer.last[low] = value & 0xFF;
+
+ emitter->raw_buffer.last += 2;
+ }
+ else
+ {
+ /* Write the character using a surrogate pair (check "reader.c"). */
+
+ value -= 0x10000;
+ emitter->raw_buffer.last[high] = 0xD8 + (value >> 18);
+ emitter->raw_buffer.last[low] = (value >> 10) & 0xFF;
+ emitter->raw_buffer.last[high+2] = 0xDC + ((value >> 8) & 0xFF);
+ emitter->raw_buffer.last[low+2] = value & 0xFF;
+
+ emitter->raw_buffer.last += 4;
+ }
+ }
+
+ /* Write the raw buffer. */
+
+ if (emitter->write_handler(emitter->write_handler_data,
+ emitter->raw_buffer.start,
+ emitter->raw_buffer.last - emitter->raw_buffer.start)) {
+ emitter->buffer.last = emitter->buffer.start;
+ emitter->buffer.pointer = emitter->buffer.start;
+ emitter->raw_buffer.last = emitter->raw_buffer.start;
+ emitter->raw_buffer.pointer = emitter->raw_buffer.start;
+ return 1;
+ }
+ else {
+ return yaml_emitter_set_writer_error(emitter, "write error");
+ }
+}
+
diff --git a/ext/psych/yaml/yaml.h b/ext/psych/yaml/yaml.h
new file mode 100644
index 0000000..f33a152
--- /dev/null
+++ b/ext/psych/yaml/yaml.h
@@ -0,0 +1,1971 @@
+/**
+ * @file yaml.h
+ * @brief Public interface for libyaml.
+ *
+ * Include the header file with the code:
+ * @code
+ * #include <yaml.h>
+ * @endcode
+ */
+
+#ifndef YAML_H
+#define YAML_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * @defgroup export Export Definitions
+ * @{
+ */
+
+/** The public API declaration. */
+
+#ifdef _WIN32
+# if defined(YAML_DECLARE_STATIC)
+# define YAML_DECLARE(type) type
+# elif defined(YAML_DECLARE_EXPORT)
+# define YAML_DECLARE(type) __declspec(dllexport) type
+# else
+# define YAML_DECLARE(type) __declspec(dllimport) type
+# endif
+#else
+# define YAML_DECLARE(type) type
+#endif
+
+/** @} */
+
+/**
+ * @defgroup version Version Information
+ * @{
+ */
+
+/**
+ * Get the library version as a string.
+ *
+ * @returns The function returns the pointer to a static string of the form
+ * @c "X.Y.Z", where @c X is the major version number, @c Y is a minor version
+ * number, and @c Z is the patch version number.
+ */
+
+YAML_DECLARE(const char *)
+yaml_get_version_string(void);
+
+/**
+ * Get the library version numbers.
+ *
+ * @param[out] major Major version number.
+ * @param[out] minor Minor version number.
+ * @param[out] patch Patch version number.
+ */
+
+YAML_DECLARE(void)
+yaml_get_version(int *major, int *minor, int *patch);
+
+/** @} */
+
+/**
+ * @defgroup basic Basic Types
+ * @{
+ */
+
+/** The character type (UTF-8 octet). */
+typedef unsigned char yaml_char_t;
+
+/** The version directive data. */
+typedef struct yaml_version_directive_s {
+ /** The major version number. */
+ int major;
+ /** The minor version number. */
+ int minor;
+} yaml_version_directive_t;
+
+/** The tag directive data. */
+typedef struct yaml_tag_directive_s {
+ /** The tag handle. */
+ yaml_char_t *handle;
+ /** The tag prefix. */
+ yaml_char_t *prefix;
+} yaml_tag_directive_t;
+
+/** The stream encoding. */
+typedef enum yaml_encoding_e {
+ /** Let the parser choose the encoding. */
+ YAML_ANY_ENCODING,
+ /** The default UTF-8 encoding. */
+ YAML_UTF8_ENCODING,
+ /** The UTF-16-LE encoding with BOM. */
+ YAML_UTF16LE_ENCODING,
+ /** The UTF-16-BE encoding with BOM. */
+ YAML_UTF16BE_ENCODING
+} yaml_encoding_t;
+
+/** Line break types. */
+
+typedef enum yaml_break_e {
+ /** Let the parser choose the break type. */
+ YAML_ANY_BREAK,
+ /** Use CR for line breaks (Mac style). */
+ YAML_CR_BREAK,
+ /** Use LN for line breaks (Unix style). */
+ YAML_LN_BREAK,
+ /** Use CR LN for line breaks (DOS style). */
+ YAML_CRLN_BREAK
+} yaml_break_t;
+
+/** Many bad things could happen with the parser and emitter. */
+typedef enum yaml_error_type_e {
+ /** No error is produced. */
+ YAML_NO_ERROR,
+
+ /** Cannot allocate or reallocate a block of memory. */
+ YAML_MEMORY_ERROR,
+
+ /** Cannot read or decode the input stream. */
+ YAML_READER_ERROR,
+ /** Cannot scan the input stream. */
+ YAML_SCANNER_ERROR,
+ /** Cannot parse the input stream. */
+ YAML_PARSER_ERROR,
+ /** Cannot compose a YAML document. */
+ YAML_COMPOSER_ERROR,
+
+ /** Cannot write to the output stream. */
+ YAML_WRITER_ERROR,
+ /** Cannot emit a YAML stream. */
+ YAML_EMITTER_ERROR
+} yaml_error_type_t;
+
+/** The pointer position. */
+typedef struct yaml_mark_s {
+ /** The position index. */
+ size_t index;
+
+ /** The position line. */
+ size_t line;
+
+ /** The position column. */
+ size_t column;
+} yaml_mark_t;
+
+/** @} */
+
+/**
+ * @defgroup styles Node Styles
+ * @{
+ */
+
+/** Scalar styles. */
+typedef enum yaml_scalar_style_e {
+ /** Let the emitter choose the style. */
+ YAML_ANY_SCALAR_STYLE,
+
+ /** The plain scalar style. */
+ YAML_PLAIN_SCALAR_STYLE,
+
+ /** The single-quoted scalar style. */
+ YAML_SINGLE_QUOTED_SCALAR_STYLE,
+ /** The double-quoted scalar style. */
+ YAML_DOUBLE_QUOTED_SCALAR_STYLE,
+
+ /** The literal scalar style. */
+ YAML_LITERAL_SCALAR_STYLE,
+ /** The folded scalar style. */
+ YAML_FOLDED_SCALAR_STYLE
+} yaml_scalar_style_t;
+
+/** Sequence styles. */
+typedef enum yaml_sequence_style_e {
+ /** Let the emitter choose the style. */
+ YAML_ANY_SEQUENCE_STYLE,
+
+ /** The block sequence style. */
+ YAML_BLOCK_SEQUENCE_STYLE,
+ /** The flow sequence style. */
+ YAML_FLOW_SEQUENCE_STYLE
+} yaml_sequence_style_t;
+
+/** Mapping styles. */
+typedef enum yaml_mapping_style_e {
+ /** Let the emitter choose the style. */
+ YAML_ANY_MAPPING_STYLE,
+
+ /** The block mapping style. */
+ YAML_BLOCK_MAPPING_STYLE,
+ /** The flow mapping style. */
+ YAML_FLOW_MAPPING_STYLE
+/* YAML_FLOW_SET_MAPPING_STYLE */
+} yaml_mapping_style_t;
+
+/** @} */
+
+/**
+ * @defgroup tokens Tokens
+ * @{
+ */
+
+/** Token types. */
+typedef enum yaml_token_type_e {
+ /** An empty token. */
+ YAML_NO_TOKEN,
+
+ /** A STREAM-START token. */
+ YAML_STREAM_START_TOKEN,
+ /** A STREAM-END token. */
+ YAML_STREAM_END_TOKEN,
+
+ /** A VERSION-DIRECTIVE token. */
+ YAML_VERSION_DIRECTIVE_TOKEN,
+ /** A TAG-DIRECTIVE token. */
+ YAML_TAG_DIRECTIVE_TOKEN,
+ /** A DOCUMENT-START token. */
+ YAML_DOCUMENT_START_TOKEN,
+ /** A DOCUMENT-END token. */
+ YAML_DOCUMENT_END_TOKEN,
+
+ /** A BLOCK-SEQUENCE-START token. */
+ YAML_BLOCK_SEQUENCE_START_TOKEN,
+ /** A BLOCK-SEQUENCE-END token. */
+ YAML_BLOCK_MAPPING_START_TOKEN,
+ /** A BLOCK-END token. */
+ YAML_BLOCK_END_TOKEN,
+
+ /** A FLOW-SEQUENCE-START token. */
+ YAML_FLOW_SEQUENCE_START_TOKEN,
+ /** A FLOW-SEQUENCE-END token. */
+ YAML_FLOW_SEQUENCE_END_TOKEN,
+ /** A FLOW-MAPPING-START token. */
+ YAML_FLOW_MAPPING_START_TOKEN,
+ /** A FLOW-MAPPING-END token. */
+ YAML_FLOW_MAPPING_END_TOKEN,
+
+ /** A BLOCK-ENTRY token. */
+ YAML_BLOCK_ENTRY_TOKEN,
+ /** A FLOW-ENTRY token. */
+ YAML_FLOW_ENTRY_TOKEN,
+ /** A KEY token. */
+ YAML_KEY_TOKEN,
+ /** A VALUE token. */
+ YAML_VALUE_TOKEN,
+
+ /** An ALIAS token. */
+ YAML_ALIAS_TOKEN,
+ /** An ANCHOR token. */
+ YAML_ANCHOR_TOKEN,
+ /** A TAG token. */
+ YAML_TAG_TOKEN,
+ /** A SCALAR token. */
+ YAML_SCALAR_TOKEN
+} yaml_token_type_t;
+
+/** The token structure. */
+typedef struct yaml_token_s {
+
+ /** The token type. */
+ yaml_token_type_t type;
+
+ /** The token data. */
+ union {
+
+ /** The stream start (for @c YAML_STREAM_START_TOKEN). */
+ struct {
+ /** The stream encoding. */
+ yaml_encoding_t encoding;
+ } stream_start;
+
+ /** The alias (for @c YAML_ALIAS_TOKEN). */
+ struct {
+ /** The alias value. */
+ yaml_char_t *value;
+ } alias;
+
+ /** The anchor (for @c YAML_ANCHOR_TOKEN). */
+ struct {
+ /** The anchor value. */
+ yaml_char_t *value;
+ } anchor;
+
+ /** The tag (for @c YAML_TAG_TOKEN). */
+ struct {
+ /** The tag handle. */
+ yaml_char_t *handle;
+ /** The tag suffix. */
+ yaml_char_t *suffix;
+ } tag;
+
+ /** The scalar value (for @c YAML_SCALAR_TOKEN). */
+ struct {
+ /** The scalar value. */
+ yaml_char_t *value;
+ /** The length of the scalar value. */
+ size_t length;
+ /** The scalar style. */
+ yaml_scalar_style_t style;
+ } scalar;
+
+ /** The version directive (for @c YAML_VERSION_DIRECTIVE_TOKEN). */
+ struct {
+ /** The major version number. */
+ int major;
+ /** The minor version number. */
+ int minor;
+ } version_directive;
+
+ /** The tag directive (for @c YAML_TAG_DIRECTIVE_TOKEN). */
+ struct {
+ /** The tag handle. */
+ yaml_char_t *handle;
+ /** The tag prefix. */
+ yaml_char_t *prefix;
+ } tag_directive;
+
+ } data;
+
+ /** The beginning of the token. */
+ yaml_mark_t start_mark;
+ /** The end of the token. */
+ yaml_mark_t end_mark;
+
+} yaml_token_t;
+
+/**
+ * Free any memory allocated for a token object.
+ *
+ * @param[in,out] token A token object.
+ */
+
+YAML_DECLARE(void)
+yaml_token_delete(yaml_token_t *token);
+
+/** @} */
+
+/**
+ * @defgroup events Events
+ * @{
+ */
+
+/** Event types. */
+typedef enum yaml_event_type_e {
+ /** An empty event. */
+ YAML_NO_EVENT,
+
+ /** A STREAM-START event. */
+ YAML_STREAM_START_EVENT,
+ /** A STREAM-END event. */
+ YAML_STREAM_END_EVENT,
+
+ /** A DOCUMENT-START event. */
+ YAML_DOCUMENT_START_EVENT,
+ /** A DOCUMENT-END event. */
+ YAML_DOCUMENT_END_EVENT,
+
+ /** An ALIAS event. */
+ YAML_ALIAS_EVENT,
+ /** A SCALAR event. */
+ YAML_SCALAR_EVENT,
+
+ /** A SEQUENCE-START event. */
+ YAML_SEQUENCE_START_EVENT,
+ /** A SEQUENCE-END event. */
+ YAML_SEQUENCE_END_EVENT,
+
+ /** A MAPPING-START event. */
+ YAML_MAPPING_START_EVENT,
+ /** A MAPPING-END event. */
+ YAML_MAPPING_END_EVENT
+} yaml_event_type_t;
+
+/** The event structure. */
+typedef struct yaml_event_s {
+
+ /** The event type. */
+ yaml_event_type_t type;
+
+ /** The event data. */
+ union {
+
+ /** The stream parameters (for @c YAML_STREAM_START_EVENT). */
+ struct {
+ /** The document encoding. */
+ yaml_encoding_t encoding;
+ } stream_start;
+
+ /** The document parameters (for @c YAML_DOCUMENT_START_EVENT). */
+ struct {
+ /** The version directive. */
+ yaml_version_directive_t *version_directive;
+
+ /** The list of tag directives. */
+ struct {
+ /** The beginning of the tag directives list. */
+ yaml_tag_directive_t *start;
+ /** The end of the tag directives list. */
+ yaml_tag_directive_t *end;
+ } tag_directives;
+
+ /** Is the document indicator implicit? */
+ int implicit;
+ } document_start;
+
+ /** The document end parameters (for @c YAML_DOCUMENT_END_EVENT). */
+ struct {
+ /** Is the document end indicator implicit? */
+ int implicit;
+ } document_end;
+
+ /** The alias parameters (for @c YAML_ALIAS_EVENT). */
+ struct {
+ /** The anchor. */
+ yaml_char_t *anchor;
+ } alias;
+
+ /** The scalar parameters (for @c YAML_SCALAR_EVENT). */
+ struct {
+ /** The anchor. */
+ yaml_char_t *anchor;
+ /** The tag. */
+ yaml_char_t *tag;
+ /** The scalar value. */
+ yaml_char_t *value;
+ /** The length of the scalar value. */
+ size_t length;
+ /** Is the tag optional for the plain style? */
+ int plain_implicit;
+ /** Is the tag optional for any non-plain style? */
+ int quoted_implicit;
+ /** The scalar style. */
+ yaml_scalar_style_t style;
+ } scalar;
+
+ /** The sequence parameters (for @c YAML_SEQUENCE_START_EVENT). */
+ struct {
+ /** The anchor. */
+ yaml_char_t *anchor;
+ /** The tag. */
+ yaml_char_t *tag;
+ /** Is the tag optional? */
+ int implicit;
+ /** The sequence style. */
+ yaml_sequence_style_t style;
+ } sequence_start;
+
+ /** The mapping parameters (for @c YAML_MAPPING_START_EVENT). */
+ struct {
+ /** The anchor. */
+ yaml_char_t *anchor;
+ /** The tag. */
+ yaml_char_t *tag;
+ /** Is the tag optional? */
+ int implicit;
+ /** The mapping style. */
+ yaml_mapping_style_t style;
+ } mapping_start;
+
+ } data;
+
+ /** The beginning of the event. */
+ yaml_mark_t start_mark;
+ /** The end of the event. */
+ yaml_mark_t end_mark;
+
+} yaml_event_t;
+
+/**
+ * Create the STREAM-START event.
+ *
+ * @param[out] event An empty event object.
+ * @param[in] encoding The stream encoding.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+
+YAML_DECLARE(int)
+yaml_stream_start_event_initialize(yaml_event_t *event,
+ yaml_encoding_t encoding);
+
+/**
+ * Create the STREAM-END event.
+ *
+ * @param[out] event An empty event object.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+
+YAML_DECLARE(int)
+yaml_stream_end_event_initialize(yaml_event_t *event);
+
+/**
+ * Create the DOCUMENT-START event.
+ *
+ * The @a implicit argument is considered as a stylistic parameter and may be
+ * ignored by the emitter.
+ *
+ * @param[out] event An empty event object.
+ * @param[in] version_directive The %YAML directive value or
+ * @c NULL.
+ * @param[in] tag_directives_start The beginning of the %TAG
+ * directives list.
+ * @param[in] tag_directives_end The end of the %TAG directives
+ * list.
+ * @param[in] implicit If the document start indicator is
+ * implicit.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+
+YAML_DECLARE(int)
+yaml_document_start_event_initialize(yaml_event_t *event,
+ yaml_version_directive_t *version_directive,
+ yaml_tag_directive_t *tag_directives_start,
+ yaml_tag_directive_t *tag_directives_end,
+ int implicit);
+
+/**
+ * Create the DOCUMENT-END event.
+ *
+ * The @a implicit argument is considered as a stylistic parameter and may be
+ * ignored by the emitter.
+ *
+ * @param[out] event An empty event object.
+ * @param[in] implicit If the document end indicator is implicit.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+
+YAML_DECLARE(int)
+yaml_document_end_event_initialize(yaml_event_t *event, int implicit);
+
+/**
+ * Create an ALIAS event.
+ *
+ * @param[out] event An empty event object.
+ * @param[in] anchor The anchor value.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+
+YAML_DECLARE(int)
+yaml_alias_event_initialize(yaml_event_t *event, yaml_char_t *anchor);
+
+/**
+ * Create a SCALAR event.
+ *
+ * The @a style argument may be ignored by the emitter.
+ *
+ * Either the @a tag attribute or one of the @a plain_implicit and
+ * @a quoted_implicit flags must be set.
+ *
+ * @param[out] event An empty event object.
+ * @param[in] anchor The scalar anchor or @c NULL.
+ * @param[in] tag The scalar tag or @c NULL.
+ * @param[in] value The scalar value.
+ * @param[in] length The length of the scalar value.
+ * @param[in] plain_implicit If the tag may be omitted for the plain
+ * style.
+ * @param[in] quoted_implicit If the tag may be omitted for any
+ * non-plain style.
+ * @param[in] style The scalar style.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+
+YAML_DECLARE(int)
+yaml_scalar_event_initialize(yaml_event_t *event,
+ yaml_char_t *anchor, yaml_char_t *tag,
+ yaml_char_t *value, int length,
+ int plain_implicit, int quoted_implicit,
+ yaml_scalar_style_t style);
+
+/**
+ * Create a SEQUENCE-START event.
+ *
+ * The @a style argument may be ignored by the emitter.
+ *
+ * Either the @a tag attribute or the @a implicit flag must be set.
+ *
+ * @param[out] event An empty event object.
+ * @param[in] anchor The sequence anchor or @c NULL.
+ * @param[in] tag The sequence tag or @c NULL.
+ * @param[in] implicit If the tag may be omitted.
+ * @param[in] style The sequence style.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+
+YAML_DECLARE(int)
+yaml_sequence_start_event_initialize(yaml_event_t *event,
+ yaml_char_t *anchor, yaml_char_t *tag, int implicit,
+ yaml_sequence_style_t style);
+
+/**
+ * Create a SEQUENCE-END event.
+ *
+ * @param[out] event An empty event object.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+
+YAML_DECLARE(int)
+yaml_sequence_end_event_initialize(yaml_event_t *event);
+
+/**
+ * Create a MAPPING-START event.
+ *
+ * The @a style argument may be ignored by the emitter.
+ *
+ * Either the @a tag attribute or the @a implicit flag must be set.
+ *
+ * @param[out] event An empty event object.
+ * @param[in] anchor The mapping anchor or @c NULL.
+ * @param[in] tag The mapping tag or @c NULL.
+ * @param[in] implicit If the tag may be omitted.
+ * @param[in] style The mapping style.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+
+YAML_DECLARE(int)
+yaml_mapping_start_event_initialize(yaml_event_t *event,
+ yaml_char_t *anchor, yaml_char_t *tag, int implicit,
+ yaml_mapping_style_t style);
+
+/**
+ * Create a MAPPING-END event.
+ *
+ * @param[out] event An empty event object.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+
+YAML_DECLARE(int)
+yaml_mapping_end_event_initialize(yaml_event_t *event);
+
+/**
+ * Free any memory allocated for an event object.
+ *
+ * @param[in,out] event An event object.
+ */
+
+YAML_DECLARE(void)
+yaml_event_delete(yaml_event_t *event);
+
+/** @} */
+
+/**
+ * @defgroup nodes Nodes
+ * @{
+ */
+
+/** The tag @c !!null with the only possible value: @c null. */
+#define YAML_NULL_TAG "tag:yaml.org,2002:null"
+/** The tag @c !!bool with the values: @c true and @c falce. */
+#define YAML_BOOL_TAG "tag:yaml.org,2002:bool"
+/** The tag @c !!str for string values. */
+#define YAML_STR_TAG "tag:yaml.org,2002:str"
+/** The tag @c !!int for integer values. */
+#define YAML_INT_TAG "tag:yaml.org,2002:int"
+/** The tag @c !!float for float values. */
+#define YAML_FLOAT_TAG "tag:yaml.org,2002:float"
+/** The tag @c !!timestamp for date and time values. */
+#define YAML_TIMESTAMP_TAG "tag:yaml.org,2002:timestamp"
+
+/** The tag @c !!seq is used to denote sequences. */
+#define YAML_SEQ_TAG "tag:yaml.org,2002:seq"
+/** The tag @c !!map is used to denote mapping. */
+#define YAML_MAP_TAG "tag:yaml.org,2002:map"
+
+/** The default scalar tag is @c !!str. */
+#define YAML_DEFAULT_SCALAR_TAG YAML_STR_TAG
+/** The default sequence tag is @c !!seq. */
+#define YAML_DEFAULT_SEQUENCE_TAG YAML_SEQ_TAG
+/** The default mapping tag is @c !!map. */
+#define YAML_DEFAULT_MAPPING_TAG YAML_MAP_TAG
+
+/** Node types. */
+typedef enum yaml_node_type_e {
+ /** An empty node. */
+ YAML_NO_NODE,
+
+ /** A scalar node. */
+ YAML_SCALAR_NODE,
+ /** A sequence node. */
+ YAML_SEQUENCE_NODE,
+ /** A mapping node. */
+ YAML_MAPPING_NODE
+} yaml_node_type_t;
+
+/** The forward definition of a document node structure. */
+typedef struct yaml_node_s yaml_node_t;
+
+/** An element of a sequence node. */
+typedef int yaml_node_item_t;
+
+/** An element of a mapping node. */
+typedef struct yaml_node_pair_s {
+ /** The key of the element. */
+ int key;
+ /** The value of the element. */
+ int value;
+} yaml_node_pair_t;
+
+/** The node structure. */
+struct yaml_node_s {
+
+ /** The node type. */
+ yaml_node_type_t type;
+
+ /** The node tag. */
+ yaml_char_t *tag;
+
+ /** The node data. */
+ union {
+
+ /** The scalar parameters (for @c YAML_SCALAR_NODE). */
+ struct {
+ /** The scalar value. */
+ yaml_char_t *value;
+ /** The length of the scalar value. */
+ size_t length;
+ /** The scalar style. */
+ yaml_scalar_style_t style;
+ } scalar;
+
+ /** The sequence parameters (for @c YAML_SEQUENCE_NODE). */
+ struct {
+ /** The stack of sequence items. */
+ struct {
+ /** The beginning of the stack. */
+ yaml_node_item_t *start;
+ /** The end of the stack. */
+ yaml_node_item_t *end;
+ /** The top of the stack. */
+ yaml_node_item_t *top;
+ } items;
+ /** The sequence style. */
+ yaml_sequence_style_t style;
+ } sequence;
+
+ /** The mapping parameters (for @c YAML_MAPPING_NODE). */
+ struct {
+ /** The stack of mapping pairs (key, value). */
+ struct {
+ /** The beginning of the stack. */
+ yaml_node_pair_t *start;
+ /** The end of the stack. */
+ yaml_node_pair_t *end;
+ /** The top of the stack. */
+ yaml_node_pair_t *top;
+ } pairs;
+ /** The mapping style. */
+ yaml_mapping_style_t style;
+ } mapping;
+
+ } data;
+
+ /** The beginning of the node. */
+ yaml_mark_t start_mark;
+ /** The end of the node. */
+ yaml_mark_t end_mark;
+
+};
+
+/** The document structure. */
+typedef struct yaml_document_s {
+
+ /** The document nodes. */
+ struct {
+ /** The beginning of the stack. */
+ yaml_node_t *start;
+ /** The end of the stack. */
+ yaml_node_t *end;
+ /** The top of the stack. */
+ yaml_node_t *top;
+ } nodes;
+
+ /** The version directive. */
+ yaml_version_directive_t *version_directive;
+
+ /** The list of tag directives. */
+ struct {
+ /** The beginning of the tag directives list. */
+ yaml_tag_directive_t *start;
+ /** The end of the tag directives list. */
+ yaml_tag_directive_t *end;
+ } tag_directives;
+
+ /** Is the document start indicator implicit? */
+ int start_implicit;
+ /** Is the document end indicator implicit? */
+ int end_implicit;
+
+ /** The beginning of the document. */
+ yaml_mark_t start_mark;
+ /** The end of the document. */
+ yaml_mark_t end_mark;
+
+} yaml_document_t;
+
+/**
+ * Create a YAML document.
+ *
+ * @param[out] document An empty document object.
+ * @param[in] version_directive The %YAML directive value or
+ * @c NULL.
+ * @param[in] tag_directives_start The beginning of the %TAG
+ * directives list.
+ * @param[in] tag_directives_end The end of the %TAG directives
+ * list.
+ * @param[in] start_implicit If the document start indicator is
+ * implicit.
+ * @param[in] end_implicit If the document end indicator is
+ * implicit.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+
+YAML_DECLARE(int)
+yaml_document_initialize(yaml_document_t *document,
+ yaml_version_directive_t *version_directive,
+ yaml_tag_directive_t *tag_directives_start,
+ yaml_tag_directive_t *tag_directives_end,
+ int start_implicit, int end_implicit);
+
+/**
+ * Delete a YAML document and all its nodes.
+ *
+ * @param[in,out] document A document object.
+ */
+
+YAML_DECLARE(void)
+yaml_document_delete(yaml_document_t *document);
+
+/**
+ * Get a node of a YAML document.
+ *
+ * The pointer returned by this function is valid until any of the functions
+ * modifying the documents are called.
+ *
+ * @param[in] document A document object.
+ * @param[in] index The node id.
+ *
+ * @returns the node objct or @c NULL if @c node_id is out of range.
+ */
+
+YAML_DECLARE(yaml_node_t *)
+yaml_document_get_node(yaml_document_t *document, int index);
+
+/**
+ * Get the root of a YAML document node.
+ *
+ * The root object is the first object added to the document.
+ *
+ * The pointer returned by this function is valid until any of the functions
+ * modifying the documents are called.
+ *
+ * An empty document produced by the parser signifies the end of a YAML
+ * stream.
+ *
+ * @param[in] document A document object.
+ *
+ * @returns the node object or @c NULL if the document is empty.
+ */
+
+YAML_DECLARE(yaml_node_t *)
+yaml_document_get_root_node(yaml_document_t *document);
+
+/**
+ * Create a SCALAR node and attach it to the document.
+ *
+ * The @a style argument may be ignored by the emitter.
+ *
+ * @param[in,out] document A document object.
+ * @param[in] tag The scalar tag.
+ * @param[in] value The scalar value.
+ * @param[in] length The length of the scalar value.
+ * @param[in] style The scalar style.
+ *
+ * @returns the node id or @c 0 on error.
+ */
+
+YAML_DECLARE(int)
+yaml_document_add_scalar(yaml_document_t *document,
+ yaml_char_t *tag, yaml_char_t *value, int length,
+ yaml_scalar_style_t style);
+
+/**
+ * Create a SEQUENCE node and attach it to the document.
+ *
+ * The @a style argument may be ignored by the emitter.
+ *
+ * @param[in,out] document A document object.
+ * @param[in] tag The sequence tag.
+ * @param[in] style The sequence style.
+ *
+ * @returns the node id or @c 0 on error.
+ */
+
+YAML_DECLARE(int)
+yaml_document_add_sequence(yaml_document_t *document,
+ yaml_char_t *tag, yaml_sequence_style_t style);
+
+/**
+ * Create a MAPPING node and attach it to the document.
+ *
+ * The @a style argument may be ignored by the emitter.
+ *
+ * @param[in,out] document A document object.
+ * @param[in] tag The sequence tag.
+ * @param[in] style The sequence style.
+ *
+ * @returns the node id or @c 0 on error.
+ */
+
+YAML_DECLARE(int)
+yaml_document_add_mapping(yaml_document_t *document,
+ yaml_char_t *tag, yaml_mapping_style_t style);
+
+/**
+ * Add an item to a SEQUENCE node.
+ *
+ * @param[in,out] document A document object.
+ * @param[in] sequence The sequence node id.
+ * @param[in] item The item node id.
+*
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+
+YAML_DECLARE(int)
+yaml_document_append_sequence_item(yaml_document_t *document,
+ int sequence, int item);
+
+/**
+ * Add a pair of a key and a value to a MAPPING node.
+ *
+ * @param[in,out] document A document object.
+ * @param[in] mapping The mapping node id.
+ * @param[in] key The key node id.
+ * @param[in] value The value node id.
+*
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+
+YAML_DECLARE(int)
+yaml_document_append_mapping_pair(yaml_document_t *document,
+ int mapping, int key, int value);
+
+/** @} */
+
+/**
+ * @defgroup parser Parser Definitions
+ * @{
+ */
+
+/**
+ * The prototype of a read handler.
+ *
+ * The read handler is called when the parser needs to read more bytes from the
+ * source. The handler should write not more than @a size bytes to the @a
+ * buffer. The number of written bytes should be set to the @a length variable.
+ *
+ * @param[in,out] data A pointer to an application data specified by
+ * yaml_parser_set_input().
+ * @param[out] buffer The buffer to write the data from the source.
+ * @param[in] size The size of the buffer.
+ * @param[out] size_read The actual number of bytes read from the source.
+ *
+ * @returns On success, the handler should return @c 1. If the handler failed,
+ * the returned value should be @c 0. On EOF, the handler should set the
+ * @a size_read to @c 0 and return @c 1.
+ */
+
+typedef int yaml_read_handler_t(void *data, unsigned char *buffer, size_t size,
+ size_t *size_read);
+
+/**
+ * This structure holds information about a potential simple key.
+ */
+
+typedef struct yaml_simple_key_s {
+ /** Is a simple key possible? */
+ int possible;
+
+ /** Is a simple key required? */
+ int required;
+
+ /** The number of the token. */
+ size_t token_number;
+
+ /** The position mark. */
+ yaml_mark_t mark;
+} yaml_simple_key_t;
+
+/**
+ * The states of the parser.
+ */
+typedef enum yaml_parser_state_e {
+ /** Expect STREAM-START. */
+ YAML_PARSE_STREAM_START_STATE,
+ /** Expect the beginning of an implicit document. */
+ YAML_PARSE_IMPLICIT_DOCUMENT_START_STATE,
+ /** Expect DOCUMENT-START. */
+ YAML_PARSE_DOCUMENT_START_STATE,
+ /** Expect the content of a document. */
+ YAML_PARSE_DOCUMENT_CONTENT_STATE,
+ /** Expect DOCUMENT-END. */
+ YAML_PARSE_DOCUMENT_END_STATE,
+ /** Expect a block node. */
+ YAML_PARSE_BLOCK_NODE_STATE,
+ /** Expect a block node or indentless sequence. */
+ YAML_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE,
+ /** Expect a flow node. */
+ YAML_PARSE_FLOW_NODE_STATE,
+ /** Expect the first entry of a block sequence. */
+ YAML_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE,
+ /** Expect an entry of a block sequence. */
+ YAML_PARSE_BLOCK_SEQUENCE_ENTRY_STATE,
+ /** Expect an entry of an indentless sequence. */
+ YAML_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE,
+ /** Expect the first key of a block mapping. */
+ YAML_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE,
+ /** Expect a block mapping key. */
+ YAML_PARSE_BLOCK_MAPPING_KEY_STATE,
+ /** Expect a block mapping value. */
+ YAML_PARSE_BLOCK_MAPPING_VALUE_STATE,
+ /** Expect the first entry of a flow sequence. */
+ YAML_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE,
+ /** Expect an entry of a flow sequence. */
+ YAML_PARSE_FLOW_SEQUENCE_ENTRY_STATE,
+ /** Expect a key of an ordered mapping. */
+ YAML_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE,
+ /** Expect a value of an ordered mapping. */
+ YAML_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE,
+ /** Expect the and of an ordered mapping entry. */
+ YAML_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE,
+ /** Expect the first key of a flow mapping. */
+ YAML_PARSE_FLOW_MAPPING_FIRST_KEY_STATE,
+ /** Expect a key of a flow mapping. */
+ YAML_PARSE_FLOW_MAPPING_KEY_STATE,
+ /** Expect a value of a flow mapping. */
+ YAML_PARSE_FLOW_MAPPING_VALUE_STATE,
+ /** Expect an empty value of a flow mapping. */
+ YAML_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE,
+ /** Expect nothing. */
+ YAML_PARSE_END_STATE
+} yaml_parser_state_t;
+
+/**
+ * This structure holds aliases data.
+ */
+
+typedef struct yaml_alias_data_s {
+ /** The anchor. */
+ yaml_char_t *anchor;
+ /** The node id. */
+ int index;
+ /** The anchor mark. */
+ yaml_mark_t mark;
+} yaml_alias_data_t;
+
+/**
+ * The parser structure.
+ *
+ * All members are internal. Manage the structure using the @c yaml_parser_
+ * family of functions.
+ */
+
+typedef struct yaml_parser_s {
+
+ /**
+ * @name Error handling
+ * @{
+ */
+
+ /** Error type. */
+ yaml_error_type_t error;
+ /** Error description. */
+ const char *problem;
+ /** The byte about which the problem occurred. */
+ size_t problem_offset;
+ /** The problematic value (@c -1 is none). */
+ int problem_value;
+ /** The problem position. */
+ yaml_mark_t problem_mark;
+ /** The error context. */
+ const char *context;
+ /** The context position. */
+ yaml_mark_t context_mark;
+
+ /**
+ * @}
+ */
+
+ /**
+ * @name Reader stuff
+ * @{
+ */
+
+ /** Read handler. */
+ yaml_read_handler_t *read_handler;
+
+ /** A pointer for passing to the read handler. */
+ void *read_handler_data;
+
+ /** Standard (string or file) input data. */
+ union {
+ /** String input data. */
+ struct {
+ /** The string start pointer. */
+ const unsigned char *start;
+ /** The string end pointer. */
+ const unsigned char *end;
+ /** The string current position. */
+ const unsigned char *current;
+ } string;
+
+ /** File input data. */
+ FILE *file;
+ } input;
+
+ /** EOF flag */
+ int eof;
+
+ /** The working buffer. */
+ struct {
+ /** The beginning of the buffer. */
+ yaml_char_t *start;
+ /** The end of the buffer. */
+ yaml_char_t *end;
+ /** The current position of the buffer. */
+ yaml_char_t *pointer;
+ /** The last filled position of the buffer. */
+ yaml_char_t *last;
+ } buffer;
+
+ /* The number of unread characters in the buffer. */
+ size_t unread;
+
+ /** The raw buffer. */
+ struct {
+ /** The beginning of the buffer. */
+ unsigned char *start;
+ /** The end of the buffer. */
+ unsigned char *end;
+ /** The current position of the buffer. */
+ unsigned char *pointer;
+ /** The last filled position of the buffer. */
+ unsigned char *last;
+ } raw_buffer;
+
+ /** The input encoding. */
+ yaml_encoding_t encoding;
+
+ /** The offset of the current position (in bytes). */
+ size_t offset;
+
+ /** The mark of the current position. */
+ yaml_mark_t mark;
+
+ /**
+ * @}
+ */
+
+ /**
+ * @name Scanner stuff
+ * @{
+ */
+
+ /** Have we started to scan the input stream? */
+ int stream_start_produced;
+
+ /** Have we reached the end of the input stream? */
+ int stream_end_produced;
+
+ /** The number of unclosed '[' and '{' indicators. */
+ int flow_level;
+
+ /** The tokens queue. */
+ struct {
+ /** The beginning of the tokens queue. */
+ yaml_token_t *start;
+ /** The end of the tokens queue. */
+ yaml_token_t *end;
+ /** The head of the tokens queue. */
+ yaml_token_t *head;
+ /** The tail of the tokens queue. */
+ yaml_token_t *tail;
+ } tokens;
+
+ /** The number of tokens fetched from the queue. */
+ size_t tokens_parsed;
+
+ /* Does the tokens queue contain a token ready for dequeueing. */
+ int token_available;
+
+ /** The indentation levels stack. */
+ struct {
+ /** The beginning of the stack. */
+ int *start;
+ /** The end of the stack. */
+ int *end;
+ /** The top of the stack. */
+ int *top;
+ } indents;
+
+ /** The current indentation level. */
+ int indent;
+
+ /** May a simple key occur at the current position? */
+ int simple_key_allowed;
+
+ /** The stack of simple keys. */
+ struct {
+ /** The beginning of the stack. */
+ yaml_simple_key_t *start;
+ /** The end of the stack. */
+ yaml_simple_key_t *end;
+ /** The top of the stack. */
+ yaml_simple_key_t *top;
+ } simple_keys;
+
+ /**
+ * @}
+ */
+
+ /**
+ * @name Parser stuff
+ * @{
+ */
+
+ /** The parser states stack. */
+ struct {
+ /** The beginning of the stack. */
+ yaml_parser_state_t *start;
+ /** The end of the stack. */
+ yaml_parser_state_t *end;
+ /** The top of the stack. */
+ yaml_parser_state_t *top;
+ } states;
+
+ /** The current parser state. */
+ yaml_parser_state_t state;
+
+ /** The stack of marks. */
+ struct {
+ /** The beginning of the stack. */
+ yaml_mark_t *start;
+ /** The end of the stack. */
+ yaml_mark_t *end;
+ /** The top of the stack. */
+ yaml_mark_t *top;
+ } marks;
+
+ /** The list of TAG directives. */
+ struct {
+ /** The beginning of the list. */
+ yaml_tag_directive_t *start;
+ /** The end of the list. */
+ yaml_tag_directive_t *end;
+ /** The top of the list. */
+ yaml_tag_directive_t *top;
+ } tag_directives;
+
+ /**
+ * @}
+ */
+
+ /**
+ * @name Dumper stuff
+ * @{
+ */
+
+ /** The alias data. */
+ struct {
+ /** The beginning of the list. */
+ yaml_alias_data_t *start;
+ /** The end of the list. */
+ yaml_alias_data_t *end;
+ /** The top of the list. */
+ yaml_alias_data_t *top;
+ } aliases;
+
+ /** The currently parsed document. */
+ yaml_document_t *document;
+
+ /**
+ * @}
+ */
+
+} yaml_parser_t;
+
+/**
+ * Initialize a parser.
+ *
+ * This function creates a new parser object. An application is responsible
+ * for destroying the object using the yaml_parser_delete() function.
+ *
+ * @param[out] parser An empty parser object.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+
+YAML_DECLARE(int)
+yaml_parser_initialize(yaml_parser_t *parser);
+
+/**
+ * Destroy a parser.
+ *
+ * @param[in,out] parser A parser object.
+ */
+
+YAML_DECLARE(void)
+yaml_parser_delete(yaml_parser_t *parser);
+
+/**
+ * Set a string input.
+ *
+ * Note that the @a input pointer must be valid while the @a parser object
+ * exists. The application is responsible for destroing @a input after
+ * destroying the @a parser.
+ *
+ * @param[in,out] parser A parser object.
+ * @param[in] input A source data.
+ * @param[in] size The length of the source data in bytes.
+ */
+
+YAML_DECLARE(void)
+yaml_parser_set_input_string(yaml_parser_t *parser,
+ const unsigned char *input, size_t size);
+
+/**
+ * Set a file input.
+ *
+ * @a file should be a file object open for reading. The application is
+ * responsible for closing the @a file.
+ *
+ * @param[in,out] parser A parser object.
+ * @param[in] file An open file.
+ */
+
+YAML_DECLARE(void)
+yaml_parser_set_input_file(yaml_parser_t *parser, FILE *file);
+
+/**
+ * Set a generic input handler.
+ *
+ * @param[in,out] parser A parser object.
+ * @param[in] handler A read handler.
+ * @param[in] data Any application data for passing to the read
+ * handler.
+ */
+
+YAML_DECLARE(void)
+yaml_parser_set_input(yaml_parser_t *parser,
+ yaml_read_handler_t *handler, void *data);
+
+/**
+ * Set the source encoding.
+ *
+ * @param[in,out] parser A parser object.
+ * @param[in] encoding The source encoding.
+ */
+
+YAML_DECLARE(void)
+yaml_parser_set_encoding(yaml_parser_t *parser, yaml_encoding_t encoding);
+
+/**
+ * Scan the input stream and produce the next token.
+ *
+ * Call the function subsequently to produce a sequence of tokens corresponding
+ * to the input stream. The initial token has the type
+ * @c YAML_STREAM_START_TOKEN while the ending token has the type
+ * @c YAML_STREAM_END_TOKEN.
+ *
+ * An application is responsible for freeing any buffers associated with the
+ * produced token object using the @c yaml_token_delete function.
+ *
+ * An application must not alternate the calls of yaml_parser_scan() with the
+ * calls of yaml_parser_parse() or yaml_parser_load(). Doing this will break
+ * the parser.
+ *
+ * @param[in,out] parser A parser object.
+ * @param[out] token An empty token object.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+
+YAML_DECLARE(int)
+yaml_parser_scan(yaml_parser_t *parser, yaml_token_t *token);
+
+/**
+ * Parse the input stream and produce the next parsing event.
+ *
+ * Call the function subsequently to produce a sequence of events corresponding
+ * to the input stream. The initial event has the type
+ * @c YAML_STREAM_START_EVENT while the ending event has the type
+ * @c YAML_STREAM_END_EVENT.
+ *
+ * An application is responsible for freeing any buffers associated with the
+ * produced event object using the yaml_event_delete() function.
+ *
+ * An application must not alternate the calls of yaml_parser_parse() with the
+ * calls of yaml_parser_scan() or yaml_parser_load(). Doing this will break the
+ * parser.
+ *
+ * @param[in,out] parser A parser object.
+ * @param[out] event An empty event object.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+
+YAML_DECLARE(int)
+yaml_parser_parse(yaml_parser_t *parser, yaml_event_t *event);
+
+/**
+ * Parse the input stream and produce the next YAML document.
+ *
+ * Call this function subsequently to produce a sequence of documents
+ * constituting the input stream.
+ *
+ * If the produced document has no root node, it means that the document
+ * end has been reached.
+ *
+ * An application is responsible for freeing any data associated with the
+ * produced document object using the yaml_document_delete() function.
+ *
+ * An application must not alternate the calls of yaml_parser_load() with the
+ * calls of yaml_parser_scan() or yaml_parser_parse(). Doing this will break
+ * the parser.
+ *
+ * @param[in,out] parser A parser object.
+ * @param[out] document An empty document object.
+ *
+ * @return @c 1 if the function succeeded, @c 0 on error.
+ */
+
+YAML_DECLARE(int)
+yaml_parser_load(yaml_parser_t *parser, yaml_document_t *document);
+
+/** @} */
+
+/**
+ * @defgroup emitter Emitter Definitions
+ * @{
+ */
+
+/**
+ * The prototype of a write handler.
+ *
+ * The write handler is called when the emitter needs to flush the accumulated
+ * characters to the output. The handler should write @a size bytes of the
+ * @a buffer to the output.
+ *
+ * @param[in,out] data A pointer to an application data specified by
+ * yaml_emitter_set_output().
+ * @param[in] buffer The buffer with bytes to be written.
+ * @param[in] size The size of the buffer.
+ *
+ * @returns On success, the handler should return @c 1. If the handler failed,
+ * the returned value should be @c 0.
+ */
+
+typedef int yaml_write_handler_t(void *data, unsigned char *buffer, size_t size);
+
+/** The emitter states. */
+typedef enum yaml_emitter_state_e {
+ /** Expect STREAM-START. */
+ YAML_EMIT_STREAM_START_STATE,
+ /** Expect the first DOCUMENT-START or STREAM-END. */
+ YAML_EMIT_FIRST_DOCUMENT_START_STATE,
+ /** Expect DOCUMENT-START or STREAM-END. */
+ YAML_EMIT_DOCUMENT_START_STATE,
+ /** Expect the content of a document. */
+ YAML_EMIT_DOCUMENT_CONTENT_STATE,
+ /** Expect DOCUMENT-END. */
+ YAML_EMIT_DOCUMENT_END_STATE,
+ /** Expect the first item of a flow sequence. */
+ YAML_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE,
+ /** Expect an item of a flow sequence. */
+ YAML_EMIT_FLOW_SEQUENCE_ITEM_STATE,
+ /** Expect the first key of a flow mapping. */
+ YAML_EMIT_FLOW_MAPPING_FIRST_KEY_STATE,
+ /** Expect a key of a flow mapping. */
+ YAML_EMIT_FLOW_MAPPING_KEY_STATE,
+ /** Expect a value for a simple key of a flow mapping. */
+ YAML_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE,
+ /** Expect a value of a flow mapping. */
+ YAML_EMIT_FLOW_MAPPING_VALUE_STATE,
+ /** Expect the first item of a block sequence. */
+ YAML_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE,
+ /** Expect an item of a block sequence. */
+ YAML_EMIT_BLOCK_SEQUENCE_ITEM_STATE,
+ /** Expect the first key of a block mapping. */
+ YAML_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE,
+ /** Expect the key of a block mapping. */
+ YAML_EMIT_BLOCK_MAPPING_KEY_STATE,
+ /** Expect a value for a simple key of a block mapping. */
+ YAML_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE,
+ /** Expect a value of a block mapping. */
+ YAML_EMIT_BLOCK_MAPPING_VALUE_STATE,
+ /** Expect nothing. */
+ YAML_EMIT_END_STATE
+} yaml_emitter_state_t;
+
+/**
+ * The emitter structure.
+ *
+ * All members are internal. Manage the structure using the @c yaml_emitter_
+ * family of functions.
+ */
+
+typedef struct yaml_emitter_s {
+
+ /**
+ * @name Error handling
+ * @{
+ */
+
+ /** Error type. */
+ yaml_error_type_t error;
+ /** Error description. */
+ const char *problem;
+
+ /**
+ * @}
+ */
+
+ /**
+ * @name Writer stuff
+ * @{
+ */
+
+ /** Write handler. */
+ yaml_write_handler_t *write_handler;
+
+ /** A pointer for passing to the white handler. */
+ void *write_handler_data;
+
+ /** Standard (string or file) output data. */
+ union {
+ /** String output data. */
+ struct {
+ /** The buffer pointer. */
+ unsigned char *buffer;
+ /** The buffer size. */
+ size_t size;
+ /** The number of written bytes. */
+ size_t *size_written;
+ } string;
+
+ /** File output data. */
+ FILE *file;
+ } output;
+
+ /** The working buffer. */
+ struct {
+ /** The beginning of the buffer. */
+ yaml_char_t *start;
+ /** The end of the buffer. */
+ yaml_char_t *end;
+ /** The current position of the buffer. */
+ yaml_char_t *pointer;
+ /** The last filled position of the buffer. */
+ yaml_char_t *last;
+ } buffer;
+
+ /** The raw buffer. */
+ struct {
+ /** The beginning of the buffer. */
+ unsigned char *start;
+ /** The end of the buffer. */
+ unsigned char *end;
+ /** The current position of the buffer. */
+ unsigned char *pointer;
+ /** The last filled position of the buffer. */
+ unsigned char *last;
+ } raw_buffer;
+
+ /** The stream encoding. */
+ yaml_encoding_t encoding;
+
+ /**
+ * @}
+ */
+
+ /**
+ * @name Emitter stuff
+ * @{
+ */
+
+ /** If the output is in the canonical style? */
+ int canonical;
+ /** The number of indentation spaces. */
+ int best_indent;
+ /** The preferred width of the output lines. */
+ int best_width;
+ /** Allow unescaped non-ASCII characters? */
+ int unicode;
+ /** The preferred line break. */
+ yaml_break_t line_break;
+
+ /** The stack of states. */
+ struct {
+ /** The beginning of the stack. */
+ yaml_emitter_state_t *start;
+ /** The end of the stack. */
+ yaml_emitter_state_t *end;
+ /** The top of the stack. */
+ yaml_emitter_state_t *top;
+ } states;
+
+ /** The current emitter state. */
+ yaml_emitter_state_t state;
+
+ /** The event queue. */
+ struct {
+ /** The beginning of the event queue. */
+ yaml_event_t *start;
+ /** The end of the event queue. */
+ yaml_event_t *end;
+ /** The head of the event queue. */
+ yaml_event_t *head;
+ /** The tail of the event queue. */
+ yaml_event_t *tail;
+ } events;
+
+ /** The stack of indentation levels. */
+ struct {
+ /** The beginning of the stack. */
+ int *start;
+ /** The end of the stack. */
+ int *end;
+ /** The top of the stack. */
+ int *top;
+ } indents;
+
+ /** The list of tag directives. */
+ struct {
+ /** The beginning of the list. */
+ yaml_tag_directive_t *start;
+ /** The end of the list. */
+ yaml_tag_directive_t *end;
+ /** The top of the list. */
+ yaml_tag_directive_t *top;
+ } tag_directives;
+
+ /** The current indentation level. */
+ int indent;
+
+ /** The current flow level. */
+ int flow_level;
+
+ /** Is it the document root context? */
+ int root_context;
+ /** Is it a sequence context? */
+ int sequence_context;
+ /** Is it a mapping context? */
+ int mapping_context;
+ /** Is it a simple mapping key context? */
+ int simple_key_context;
+
+ /** The current line. */
+ int line;
+ /** The current column. */
+ int column;
+ /** If the last character was a whitespace? */
+ int whitespace;
+ /** If the last character was an indentation character (' ', '-', '?', ':')? */
+ int indention;
+ /** If an explicit document end is required? */
+ int open_ended;
+
+ /** Anchor analysis. */
+ struct {
+ /** The anchor value. */
+ yaml_char_t *anchor;
+ /** The anchor length. */
+ size_t anchor_length;
+ /** Is it an alias? */
+ int alias;
+ } anchor_data;
+
+ /** Tag analysis. */
+ struct {
+ /** The tag handle. */
+ yaml_char_t *handle;
+ /** The tag handle length. */
+ size_t handle_length;
+ /** The tag suffix. */
+ yaml_char_t *suffix;
+ /** The tag suffix length. */
+ size_t suffix_length;
+ } tag_data;
+
+ /** Scalar analysis. */
+ struct {
+ /** The scalar value. */
+ yaml_char_t *value;
+ /** The scalar length. */
+ size_t length;
+ /** Does the scalar contain line breaks? */
+ int multiline;
+ /** Can the scalar be expessed in the flow plain style? */
+ int flow_plain_allowed;
+ /** Can the scalar be expressed in the block plain style? */
+ int block_plain_allowed;
+ /** Can the scalar be expressed in the single quoted style? */
+ int single_quoted_allowed;
+ /** Can the scalar be expressed in the literal or folded styles? */
+ int block_allowed;
+ /** The output style. */
+ yaml_scalar_style_t style;
+ } scalar_data;
+
+ /**
+ * @}
+ */
+
+ /**
+ * @name Dumper stuff
+ * @{
+ */
+
+ /** If the stream was already opened? */
+ int opened;
+ /** If the stream was already closed? */
+ int closed;
+
+ /** The information associated with the document nodes. */
+ struct {
+ /** The number of references. */
+ int references;
+ /** The anchor id. */
+ int anchor;
+ /** If the node has been emitted? */
+ int serialized;
+ } *anchors;
+
+ /** The last assigned anchor id. */
+ int last_anchor_id;
+
+ /** The currently emitted document. */
+ yaml_document_t *document;
+
+ /**
+ * @}
+ */
+
+} yaml_emitter_t;
+
+/**
+ * Initialize an emitter.
+ *
+ * This function creates a new emitter object. An application is responsible
+ * for destroying the object using the yaml_emitter_delete() function.
+ *
+ * @param[out] emitter An empty parser object.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+
+YAML_DECLARE(int)
+yaml_emitter_initialize(yaml_emitter_t *emitter);
+
+/**
+ * Destroy an emitter.
+ *
+ * @param[in,out] emitter An emitter object.
+ */
+
+YAML_DECLARE(void)
+yaml_emitter_delete(yaml_emitter_t *emitter);
+
+/**
+ * Set a string output.
+ *
+ * The emitter will write the output characters to the @a output buffer of the
+ * size @a size. The emitter will set @a size_written to the number of written
+ * bytes. If the buffer is smaller than required, the emitter produces the
+ * YAML_WRITE_ERROR error.
+ *
+ * @param[in,out] emitter An emitter object.
+ * @param[in] output An output buffer.
+ * @param[in] size The buffer size.
+ * @param[in] size_written The pointer to save the number of written
+ * bytes.
+ */
+
+YAML_DECLARE(void)
+yaml_emitter_set_output_string(yaml_emitter_t *emitter,
+ unsigned char *output, size_t size, size_t *size_written);
+
+/**
+ * Set a file output.
+ *
+ * @a file should be a file object open for writing. The application is
+ * responsible for closing the @a file.
+ *
+ * @param[in,out] emitter An emitter object.
+ * @param[in] file An open file.
+ */
+
+YAML_DECLARE(void)
+yaml_emitter_set_output_file(yaml_emitter_t *emitter, FILE *file);
+
+/**
+ * Set a generic output handler.
+ *
+ * @param[in,out] emitter An emitter object.
+ * @param[in] handler A write handler.
+ * @param[in] data Any application data for passing to the write
+ * handler.
+ */
+
+YAML_DECLARE(void)
+yaml_emitter_set_output(yaml_emitter_t *emitter,
+ yaml_write_handler_t *handler, void *data);
+
+/**
+ * Set the output encoding.
+ *
+ * @param[in,out] emitter An emitter object.
+ * @param[in] encoding The output encoding.
+ */
+
+YAML_DECLARE(void)
+yaml_emitter_set_encoding(yaml_emitter_t *emitter, yaml_encoding_t encoding);
+
+/**
+ * Set if the output should be in the "canonical" format as in the YAML
+ * specification.
+ *
+ * @param[in,out] emitter An emitter object.
+ * @param[in] canonical If the output is canonical.
+ */
+
+YAML_DECLARE(void)
+yaml_emitter_set_canonical(yaml_emitter_t *emitter, int canonical);
+
+/**
+ * Set the indentation increment.
+ *
+ * @param[in,out] emitter An emitter object.
+ * @param[in] indent The indentation increment (1 < . < 10).
+ */
+
+YAML_DECLARE(void)
+yaml_emitter_set_indent(yaml_emitter_t *emitter, int indent);
+
+/**
+ * Set the preferred line width. @c -1 means unlimited.
+ *
+ * @param[in,out] emitter An emitter object.
+ * @param[in] width The preferred line width.
+ */
+
+YAML_DECLARE(void)
+yaml_emitter_set_width(yaml_emitter_t *emitter, int width);
+
+/**
+ * Set if unescaped non-ASCII characters are allowed.
+ *
+ * @param[in,out] emitter An emitter object.
+ * @param[in] unicode If unescaped Unicode characters are allowed.
+ */
+
+YAML_DECLARE(void)
+yaml_emitter_set_unicode(yaml_emitter_t *emitter, int unicode);
+
+/**
+ * Set the preferred line break.
+ *
+ * @param[in,out] emitter An emitter object.
+ * @param[in] line_break The preferred line break.
+ */
+
+YAML_DECLARE(void)
+yaml_emitter_set_break(yaml_emitter_t *emitter, yaml_break_t line_break);
+
+/**
+ * Emit an event.
+ *
+ * The event object may be generated using the yaml_parser_parse() function.
+ * The emitter takes the responsibility for the event object and destroys its
+ * content after it is emitted. The event object is destroyed even if the
+ * function fails.
+ *
+ * @param[in,out] emitter An emitter object.
+ * @param[in,out] event An event object.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+
+YAML_DECLARE(int)
+yaml_emitter_emit(yaml_emitter_t *emitter, yaml_event_t *event);
+
+/**
+ * Start a YAML stream.
+ *
+ * This function should be used before yaml_emitter_dump() is called.
+ *
+ * @param[in,out] emitter An emitter object.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+
+YAML_DECLARE(int)
+yaml_emitter_open(yaml_emitter_t *emitter);
+
+/**
+ * Finish a YAML stream.
+ *
+ * This function should be used after yaml_emitter_dump() is called.
+ *
+ * @param[in,out] emitter An emitter object.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+
+YAML_DECLARE(int)
+yaml_emitter_close(yaml_emitter_t *emitter);
+
+/**
+ * Emit a YAML document.
+ *
+ * The documen object may be generated using the yaml_parser_load() function
+ * or the yaml_document_initialize() function. The emitter takes the
+ * responsibility for the document object and destoys its content after
+ * it is emitted. The document object is destroyedeven if the function fails.
+ *
+ * @param[in,out] emitter An emitter object.
+ * @param[in,out] document A document object.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+
+YAML_DECLARE(int)
+yaml_emitter_dump(yaml_emitter_t *emitter, yaml_document_t *document);
+
+/**
+ * Flush the accumulated characters to the output.
+ *
+ * @param[in,out] emitter An emitter object.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+
+YAML_DECLARE(int)
+yaml_emitter_flush(yaml_emitter_t *emitter);
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef YAML_H */
+
diff --git a/ext/psych/yaml/yaml_private.h b/ext/psych/yaml/yaml_private.h
new file mode 100644
index 0000000..944499c
--- /dev/null
+++ b/ext/psych/yaml/yaml_private.h
@@ -0,0 +1,664 @@
+#ifdef RUBY_EXTCONF_H
+#include RUBY_EXTCONF_H
+#endif
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <yaml.h>
+
+#include <assert.h>
+#include <limits.h>
+#include <stddef.h>
+
+#ifndef _MSC_VER
+#include <stdint.h>
+#else
+#ifdef _WIN64
+#define PTRDIFF_MAX _I64_MAX
+#else
+#define PTRDIFF_MAX INT_MAX
+#endif
+#endif
+
+/*
+ * Memory management.
+ */
+
+YAML_DECLARE(void *)
+yaml_malloc(size_t size);
+
+YAML_DECLARE(void *)
+yaml_realloc(void *ptr, size_t size);
+
+YAML_DECLARE(void)
+yaml_free(void *ptr);
+
+YAML_DECLARE(yaml_char_t *)
+yaml_strdup(const yaml_char_t *);
+
+/*
+ * Reader: Ensure that the buffer contains at least `length` characters.
+ */
+
+YAML_DECLARE(int)
+yaml_parser_update_buffer(yaml_parser_t *parser, size_t length);
+
+/*
+ * Scanner: Ensure that the token stack contains at least one token ready.
+ */
+
+YAML_DECLARE(int)
+yaml_parser_fetch_more_tokens(yaml_parser_t *parser);
+
+/*
+ * The size of the input raw buffer.
+ */
+
+#define INPUT_RAW_BUFFER_SIZE 16384
+
+/*
+ * The size of the input buffer.
+ *
+ * It should be possible to decode the whole raw buffer.
+ */
+
+#define INPUT_BUFFER_SIZE (INPUT_RAW_BUFFER_SIZE*3)
+
+/*
+ * The size of the output buffer.
+ */
+
+#define OUTPUT_BUFFER_SIZE 16384
+
+/*
+ * The size of the output raw buffer.
+ *
+ * It should be possible to encode the whole output buffer.
+ */
+
+#define OUTPUT_RAW_BUFFER_SIZE (OUTPUT_BUFFER_SIZE*2+2)
+
+/*
+ * The size of other stacks and queues.
+ */
+
+#define INITIAL_STACK_SIZE 16
+#define INITIAL_QUEUE_SIZE 16
+#define INITIAL_STRING_SIZE 16
+
+/*
+ * Buffer management.
+ */
+
+#define BUFFER_INIT(context,buffer,size) \
+ (((buffer).start = yaml_malloc(size)) ? \
+ ((buffer).last = (buffer).pointer = (buffer).start, \
+ (buffer).end = (buffer).start+(size), \
+ 1) : \
+ ((context)->error = YAML_MEMORY_ERROR, \
+ 0))
+
+#define BUFFER_DEL(context,buffer) \
+ (yaml_free((buffer).start), \
+ (buffer).start = (buffer).pointer = (buffer).end = 0)
+
+/*
+ * String management.
+ */
+
+typedef struct {
+ yaml_char_t *start;
+ yaml_char_t *end;
+ yaml_char_t *pointer;
+} yaml_string_t;
+
+YAML_DECLARE(int)
+yaml_string_extend(yaml_char_t **start,
+ yaml_char_t **pointer, yaml_char_t **end);
+
+YAML_DECLARE(int)
+yaml_string_join(
+ yaml_char_t **a_start, yaml_char_t **a_pointer, yaml_char_t **a_end,
+ yaml_char_t **b_start, yaml_char_t **b_pointer, yaml_char_t **b_end);
+
+#define NULL_STRING { NULL, NULL, NULL }
+
+#define STRING(string,length) { (string), (string)+(length), (string) }
+
+#define STRING_ASSIGN(value,string,length) \
+ ((value).start = (string), \
+ (value).end = (string)+(length), \
+ (value).pointer = (string))
+
+#define STRING_INIT(context,string,size) \
+ (((string).start = yaml_malloc(size)) ? \
+ ((string).pointer = (string).start, \
+ (string).end = (string).start+(size), \
+ memset((string).start, 0, (size)), \
+ 1) : \
+ ((context)->error = YAML_MEMORY_ERROR, \
+ 0))
+
+#define STRING_DEL(context,string) \
+ (yaml_free((string).start), \
+ (string).start = (string).pointer = (string).end = 0)
+
+#define STRING_EXTEND(context,string) \
+ ((((string).pointer+5 < (string).end) \
+ || yaml_string_extend(&(string).start, \
+ &(string).pointer, &(string).end)) ? \
+ 1 : \
+ ((context)->error = YAML_MEMORY_ERROR, \
+ 0))
+
+#define CLEAR(context,string) \
+ ((string).pointer = (string).start, \
+ memset((string).start, 0, (string).end-(string).start))
+
+#define JOIN(context,string_a,string_b) \
+ ((yaml_string_join(&(string_a).start, &(string_a).pointer, \
+ &(string_a).end, &(string_b).start, \
+ &(string_b).pointer, &(string_b).end)) ? \
+ ((string_b).pointer = (string_b).start, \
+ 1) : \
+ ((context)->error = YAML_MEMORY_ERROR, \
+ 0))
+
+/*
+ * String check operations.
+ */
+
+/*
+ * Check the octet at the specified position.
+ */
+
+#define CHECK_AT(string,octet,offset) \
+ ((string).pointer[offset] == (yaml_char_t)(octet))
+
+/*
+ * Check the current octet in the buffer.
+ */
+
+#define CHECK(string,octet) CHECK_AT((string),(octet),0)
+
+/*
+ * Check if the character at the specified position is an alphabetical
+ * character, a digit, '_', or '-'.
+ */
+
+#define IS_ALPHA_AT(string,offset) \
+ (((string).pointer[offset] >= (yaml_char_t) '0' && \
+ (string).pointer[offset] <= (yaml_char_t) '9') || \
+ ((string).pointer[offset] >= (yaml_char_t) 'A' && \
+ (string).pointer[offset] <= (yaml_char_t) 'Z') || \
+ ((string).pointer[offset] >= (yaml_char_t) 'a' && \
+ (string).pointer[offset] <= (yaml_char_t) 'z') || \
+ (string).pointer[offset] == '_' || \
+ (string).pointer[offset] == '-')
+
+#define IS_ALPHA(string) IS_ALPHA_AT((string),0)
+
+/*
+ * Check if the character at the specified position is a digit.
+ */
+
+#define IS_DIGIT_AT(string,offset) \
+ (((string).pointer[offset] >= (yaml_char_t) '0' && \
+ (string).pointer[offset] <= (yaml_char_t) '9'))
+
+#define IS_DIGIT(string) IS_DIGIT_AT((string),0)
+
+/*
+ * Get the value of a digit.
+ */
+
+#define AS_DIGIT_AT(string,offset) \
+ ((string).pointer[offset] - (yaml_char_t) '0')
+
+#define AS_DIGIT(string) AS_DIGIT_AT((string),0)
+
+/*
+ * Check if the character at the specified position is a hex-digit.
+ */
+
+#define IS_HEX_AT(string,offset) \
+ (((string).pointer[offset] >= (yaml_char_t) '0' && \
+ (string).pointer[offset] <= (yaml_char_t) '9') || \
+ ((string).pointer[offset] >= (yaml_char_t) 'A' && \
+ (string).pointer[offset] <= (yaml_char_t) 'F') || \
+ ((string).pointer[offset] >= (yaml_char_t) 'a' && \
+ (string).pointer[offset] <= (yaml_char_t) 'f'))
+
+#define IS_HEX(string) IS_HEX_AT((string),0)
+
+/*
+ * Get the value of a hex-digit.
+ */
+
+#define AS_HEX_AT(string,offset) \
+ (((string).pointer[offset] >= (yaml_char_t) 'A' && \
+ (string).pointer[offset] <= (yaml_char_t) 'F') ? \
+ ((string).pointer[offset] - (yaml_char_t) 'A' + 10) : \
+ ((string).pointer[offset] >= (yaml_char_t) 'a' && \
+ (string).pointer[offset] <= (yaml_char_t) 'f') ? \
+ ((string).pointer[offset] - (yaml_char_t) 'a' + 10) : \
+ ((string).pointer[offset] - (yaml_char_t) '0'))
+
+#define AS_HEX(string) AS_HEX_AT((string),0)
+
+/*
+ * Check if the character is ASCII.
+ */
+
+#define IS_ASCII_AT(string,offset) \
+ ((string).pointer[offset] <= (yaml_char_t) '\x7F')
+
+#define IS_ASCII(string) IS_ASCII_AT((string),0)
+
+/*
+ * Check if the character can be printed unescaped.
+ */
+
+#define IS_PRINTABLE_AT(string,offset) \
+ (((string).pointer[offset] == 0x0A) /* . == #x0A */ \
+ || ((string).pointer[offset] >= 0x20 /* #x20 <= . <= #x7E */ \
+ && (string).pointer[offset] <= 0x7E) \
+ || ((string).pointer[offset] == 0xC2 /* #0xA0 <= . <= #xD7FF */ \
+ && (string).pointer[offset+1] >= 0xA0) \
+ || ((string).pointer[offset] > 0xC2 \
+ && (string).pointer[offset] < 0xED) \
+ || ((string).pointer[offset] == 0xED \
+ && (string).pointer[offset+1] < 0xA0) \
+ || ((string).pointer[offset] == 0xEE) \
+ || ((string).pointer[offset] == 0xEF /* #xE000 <= . <= #xFFFD */ \
+ && !((string).pointer[offset+1] == 0xBB /* && . != #xFEFF */ \
+ && (string).pointer[offset+2] == 0xBF) \
+ && !((string).pointer[offset+1] == 0xBF \
+ && ((string).pointer[offset+2] == 0xBE \
+ || (string).pointer[offset+2] == 0xBF))))
+
+#define IS_PRINTABLE(string) IS_PRINTABLE_AT((string),0)
+
+/*
+ * Check if the character at the specified position is NUL.
+ */
+
+#define IS_Z_AT(string,offset) CHECK_AT((string),'\0',(offset))
+
+#define IS_Z(string) IS_Z_AT((string),0)
+
+/*
+ * Check if the character at the specified position is BOM.
+ */
+
+#define IS_BOM_AT(string,offset) \
+ (CHECK_AT((string),'\xEF',(offset)) \
+ && CHECK_AT((string),'\xBB',(offset)+1) \
+ && CHECK_AT((string),'\xBF',(offset)+2)) /* BOM (#xFEFF) */
+
+#define IS_BOM(string) IS_BOM_AT(string,0)
+
+/*
+ * Check if the character at the specified position is space.
+ */
+
+#define IS_SPACE_AT(string,offset) CHECK_AT((string),' ',(offset))
+
+#define IS_SPACE(string) IS_SPACE_AT((string),0)
+
+/*
+ * Check if the character at the specified position is tab.
+ */
+
+#define IS_TAB_AT(string,offset) CHECK_AT((string),'\t',(offset))
+
+#define IS_TAB(string) IS_TAB_AT((string),0)
+
+/*
+ * Check if the character at the specified position is blank (space or tab).
+ */
+
+#define IS_BLANK_AT(string,offset) \
+ (IS_SPACE_AT((string),(offset)) || IS_TAB_AT((string),(offset)))
+
+#define IS_BLANK(string) IS_BLANK_AT((string),0)
+
+/*
+ * Check if the character at the specified position is a line break.
+ */
+
+#define IS_BREAK_AT(string,offset) \
+ (CHECK_AT((string),'\r',(offset)) /* CR (#xD)*/ \
+ || CHECK_AT((string),'\n',(offset)) /* LF (#xA) */ \
+ || (CHECK_AT((string),'\xC2',(offset)) \
+ && CHECK_AT((string),'\x85',(offset)+1)) /* NEL (#x85) */ \
+ || (CHECK_AT((string),'\xE2',(offset)) \
+ && CHECK_AT((string),'\x80',(offset)+1) \
+ && CHECK_AT((string),'\xA8',(offset)+2)) /* LS (#x2028) */ \
+ || (CHECK_AT((string),'\xE2',(offset)) \
+ && CHECK_AT((string),'\x80',(offset)+1) \
+ && CHECK_AT((string),'\xA9',(offset)+2))) /* PS (#x2029) */
+
+#define IS_BREAK(string) IS_BREAK_AT((string),0)
+
+#define IS_CRLF_AT(string,offset) \
+ (CHECK_AT((string),'\r',(offset)) && CHECK_AT((string),'\n',(offset)+1))
+
+#define IS_CRLF(string) IS_CRLF_AT((string),0)
+
+/*
+ * Check if the character is a line break or NUL.
+ */
+
+#define IS_BREAKZ_AT(string,offset) \
+ (IS_BREAK_AT((string),(offset)) || IS_Z_AT((string),(offset)))
+
+#define IS_BREAKZ(string) IS_BREAKZ_AT((string),0)
+
+/*
+ * Check if the character is a line break, space, or NUL.
+ */
+
+#define IS_SPACEZ_AT(string,offset) \
+ (IS_SPACE_AT((string),(offset)) || IS_BREAKZ_AT((string),(offset)))
+
+#define IS_SPACEZ(string) IS_SPACEZ_AT((string),0)
+
+/*
+ * Check if the character is a line break, space, tab, or NUL.
+ */
+
+#define IS_BLANKZ_AT(string,offset) \
+ (IS_BLANK_AT((string),(offset)) || IS_BREAKZ_AT((string),(offset)))
+
+#define IS_BLANKZ(string) IS_BLANKZ_AT((string),0)
+
+/*
+ * Determine the width of the character.
+ */
+
+#define WIDTH_AT(string,offset) \
+ (((string).pointer[offset] & 0x80) == 0x00 ? 1 : \
+ ((string).pointer[offset] & 0xE0) == 0xC0 ? 2 : \
+ ((string).pointer[offset] & 0xF0) == 0xE0 ? 3 : \
+ ((string).pointer[offset] & 0xF8) == 0xF0 ? 4 : 0)
+
+#define WIDTH(string) WIDTH_AT((string),0)
+
+/*
+ * Move the string pointer to the next character.
+ */
+
+#define MOVE(string) ((string).pointer += WIDTH((string)))
+
+/*
+ * Copy a character and move the pointers of both strings.
+ */
+
+#define COPY(string_a,string_b) \
+ ((*(string_b).pointer & 0x80) == 0x00 ? \
+ (*((string_a).pointer++) = *((string_b).pointer++)) : \
+ (*(string_b).pointer & 0xE0) == 0xC0 ? \
+ (*((string_a).pointer++) = *((string_b).pointer++), \
+ *((string_a).pointer++) = *((string_b).pointer++)) : \
+ (*(string_b).pointer & 0xF0) == 0xE0 ? \
+ (*((string_a).pointer++) = *((string_b).pointer++), \
+ *((string_a).pointer++) = *((string_b).pointer++), \
+ *((string_a).pointer++) = *((string_b).pointer++)) : \
+ (*(string_b).pointer & 0xF8) == 0xF0 ? \
+ (*((string_a).pointer++) = *((string_b).pointer++), \
+ *((string_a).pointer++) = *((string_b).pointer++), \
+ *((string_a).pointer++) = *((string_b).pointer++), \
+ *((string_a).pointer++) = *((string_b).pointer++)) : 0)
+
+/*
+ * Stack and queue management.
+ */
+
+YAML_DECLARE(int)
+yaml_stack_extend(void **start, void **top, void **end);
+
+YAML_DECLARE(int)
+yaml_queue_extend(void **start, void **head, void **tail, void **end);
+
+#define STACK_INIT(context,stack,size) \
+ (((stack).start = yaml_malloc((size)*sizeof(*(stack).start))) ? \
+ ((stack).top = (stack).start, \
+ (stack).end = (stack).start+(size), \
+ 1) : \
+ ((context)->error = YAML_MEMORY_ERROR, \
+ 0))
+
+#define STACK_DEL(context,stack) \
+ (yaml_free((stack).start), \
+ (stack).start = (stack).top = (stack).end = 0)
+
+#define STACK_EMPTY(context,stack) \
+ ((void)(context), \
+ ((stack).start == (stack).top))
+
+#define STACK_LIMIT(context,stack,size) \
+ ((stack).top - (stack).start < (size) ? \
+ 1 : \
+ ((context)->error = YAML_MEMORY_ERROR, \
+ 0))
+
+#define PUSH(context,stack,value) \
+ (((stack).top != (stack).end \
+ || yaml_stack_extend((void **)&(stack).start, \
+ (void **)&(stack).top, (void **)&(stack).end)) ? \
+ (*((stack).top++) = value, \
+ 1) : \
+ ((context)->error = YAML_MEMORY_ERROR, \
+ 0))
+
+#define POP(context,stack) \
+ (*(--(stack).top))
+
+#define QUEUE_INIT(context,queue,size) \
+ (((queue).start = yaml_malloc((size)*sizeof(*(queue).start))) ? \
+ ((queue).head = (queue).tail = (queue).start, \
+ (queue).end = (queue).start+(size), \
+ 1) : \
+ ((context)->error = YAML_MEMORY_ERROR, \
+ 0))
+
+#define QUEUE_DEL(context,queue) \
+ (yaml_free((queue).start), \
+ (queue).start = (queue).head = (queue).tail = (queue).end = 0)
+
+#define QUEUE_EMPTY(context,queue) \
+ ((queue).head == (queue).tail)
+
+#define ENQUEUE(context,queue,value) \
+ (((queue).tail != (queue).end \
+ || yaml_queue_extend((void **)&(queue).start, (void **)&(queue).head, \
+ (void **)&(queue).tail, (void **)&(queue).end)) ? \
+ (*((queue).tail++) = value, \
+ 1) : \
+ ((context)->error = YAML_MEMORY_ERROR, \
+ 0))
+
+#define DEQUEUE(context,queue) \
+ (*((queue).head++))
+
+#define QUEUE_INSERT(context,queue,index,value) \
+ (((queue).tail != (queue).end \
+ || yaml_queue_extend((void **)&(queue).start, (void **)&(queue).head, \
+ (void **)&(queue).tail, (void **)&(queue).end)) ? \
+ (memmove((queue).head+(index)+1,(queue).head+(index), \
+ ((queue).tail-(queue).head-(index))*sizeof(*(queue).start)), \
+ *((queue).head+(index)) = value, \
+ (queue).tail++, \
+ 1) : \
+ ((context)->error = YAML_MEMORY_ERROR, \
+ 0))
+
+/*
+ * Token initializers.
+ */
+
+#define TOKEN_INIT(token,token_type,token_start_mark,token_end_mark) \
+ (memset(&(token), 0, sizeof(yaml_token_t)), \
+ (token).type = (token_type), \
+ (token).start_mark = (token_start_mark), \
+ (token).end_mark = (token_end_mark))
+
+#define STREAM_START_TOKEN_INIT(token,token_encoding,start_mark,end_mark) \
+ (TOKEN_INIT((token),YAML_STREAM_START_TOKEN,(start_mark),(end_mark)), \
+ (token).data.stream_start.encoding = (token_encoding))
+
+#define STREAM_END_TOKEN_INIT(token,start_mark,end_mark) \
+ (TOKEN_INIT((token),YAML_STREAM_END_TOKEN,(start_mark),(end_mark)))
+
+#define ALIAS_TOKEN_INIT(token,token_value,start_mark,end_mark) \
+ (TOKEN_INIT((token),YAML_ALIAS_TOKEN,(start_mark),(end_mark)), \
+ (token).data.alias.value = (token_value))
+
+#define ANCHOR_TOKEN_INIT(token,token_value,start_mark,end_mark) \
+ (TOKEN_INIT((token),YAML_ANCHOR_TOKEN,(start_mark),(end_mark)), \
+ (token).data.anchor.value = (token_value))
+
+#define TAG_TOKEN_INIT(token,token_handle,token_suffix,start_mark,end_mark) \
+ (TOKEN_INIT((token),YAML_TAG_TOKEN,(start_mark),(end_mark)), \
+ (token).data.tag.handle = (token_handle), \
+ (token).data.tag.suffix = (token_suffix))
+
+#define SCALAR_TOKEN_INIT(token,token_value,token_length,token_style,start_mark,end_mark) \
+ (TOKEN_INIT((token),YAML_SCALAR_TOKEN,(start_mark),(end_mark)), \
+ (token).data.scalar.value = (token_value), \
+ (token).data.scalar.length = (token_length), \
+ (token).data.scalar.style = (token_style))
+
+#define VERSION_DIRECTIVE_TOKEN_INIT(token,token_major,token_minor,start_mark,end_mark) \
+ (TOKEN_INIT((token),YAML_VERSION_DIRECTIVE_TOKEN,(start_mark),(end_mark)), \
+ (token).data.version_directive.major = (token_major), \
+ (token).data.version_directive.minor = (token_minor))
+
+#define TAG_DIRECTIVE_TOKEN_INIT(token,token_handle,token_prefix,start_mark,end_mark) \
+ (TOKEN_INIT((token),YAML_TAG_DIRECTIVE_TOKEN,(start_mark),(end_mark)), \
+ (token).data.tag_directive.handle = (token_handle), \
+ (token).data.tag_directive.prefix = (token_prefix))
+
+/*
+ * Event initializers.
+ */
+
+#define EVENT_INIT(event,event_type,event_start_mark,event_end_mark) \
+ (memset(&(event), 0, sizeof(yaml_event_t)), \
+ (event).type = (event_type), \
+ (event).start_mark = (event_start_mark), \
+ (event).end_mark = (event_end_mark))
+
+#define STREAM_START_EVENT_INIT(event,event_encoding,start_mark,end_mark) \
+ (EVENT_INIT((event),YAML_STREAM_START_EVENT,(start_mark),(end_mark)), \
+ (event).data.stream_start.encoding = (event_encoding))
+
+#define STREAM_END_EVENT_INIT(event,start_mark,end_mark) \
+ (EVENT_INIT((event),YAML_STREAM_END_EVENT,(start_mark),(end_mark)))
+
+#define DOCUMENT_START_EVENT_INIT(event,event_version_directive, \
+ event_tag_directives_start,event_tag_directives_end,event_implicit,start_mark,end_mark) \
+ (EVENT_INIT((event),YAML_DOCUMENT_START_EVENT,(start_mark),(end_mark)), \
+ (event).data.document_start.version_directive = (event_version_directive), \
+ (event).data.document_start.tag_directives.start = (event_tag_directives_start), \
+ (event).data.document_start.tag_directives.end = (event_tag_directives_end), \
+ (event).data.document_start.implicit = (event_implicit))
+
+#define DOCUMENT_END_EVENT_INIT(event,event_implicit,start_mark,end_mark) \
+ (EVENT_INIT((event),YAML_DOCUMENT_END_EVENT,(start_mark),(end_mark)), \
+ (event).data.document_end.implicit = (event_implicit))
+
+#define ALIAS_EVENT_INIT(event,event_anchor,start_mark,end_mark) \
+ (EVENT_INIT((event),YAML_ALIAS_EVENT,(start_mark),(end_mark)), \
+ (event).data.alias.anchor = (event_anchor))
+
+#define SCALAR_EVENT_INIT(event,event_anchor,event_tag,event_value,event_length, \
+ event_plain_implicit, event_quoted_implicit,event_style,start_mark,end_mark) \
+ (EVENT_INIT((event),YAML_SCALAR_EVENT,(start_mark),(end_mark)), \
+ (event).data.scalar.anchor = (event_anchor), \
+ (event).data.scalar.tag = (event_tag), \
+ (event).data.scalar.value = (event_value), \
+ (event).data.scalar.length = (event_length), \
+ (event).data.scalar.plain_implicit = (event_plain_implicit), \
+ (event).data.scalar.quoted_implicit = (event_quoted_implicit), \
+ (event).data.scalar.style = (event_style))
+
+#define SEQUENCE_START_EVENT_INIT(event,event_anchor,event_tag, \
+ event_implicit,event_style,start_mark,end_mark) \
+ (EVENT_INIT((event),YAML_SEQUENCE_START_EVENT,(start_mark),(end_mark)), \
+ (event).data.sequence_start.anchor = (event_anchor), \
+ (event).data.sequence_start.tag = (event_tag), \
+ (event).data.sequence_start.implicit = (event_implicit), \
+ (event).data.sequence_start.style = (event_style))
+
+#define SEQUENCE_END_EVENT_INIT(event,start_mark,end_mark) \
+ (EVENT_INIT((event),YAML_SEQUENCE_END_EVENT,(start_mark),(end_mark)))
+
+#define MAPPING_START_EVENT_INIT(event,event_anchor,event_tag, \
+ event_implicit,event_style,start_mark,end_mark) \
+ (EVENT_INIT((event),YAML_MAPPING_START_EVENT,(start_mark),(end_mark)), \
+ (event).data.mapping_start.anchor = (event_anchor), \
+ (event).data.mapping_start.tag = (event_tag), \
+ (event).data.mapping_start.implicit = (event_implicit), \
+ (event).data.mapping_start.style = (event_style))
+
+#define MAPPING_END_EVENT_INIT(event,start_mark,end_mark) \
+ (EVENT_INIT((event),YAML_MAPPING_END_EVENT,(start_mark),(end_mark)))
+
+/*
+ * Document initializer.
+ */
+
+#define DOCUMENT_INIT(document,document_nodes_start,document_nodes_end, \
+ document_version_directive,document_tag_directives_start, \
+ document_tag_directives_end,document_start_implicit, \
+ document_end_implicit,document_start_mark,document_end_mark) \
+ (memset(&(document), 0, sizeof(yaml_document_t)), \
+ (document).nodes.start = (document_nodes_start), \
+ (document).nodes.end = (document_nodes_end), \
+ (document).nodes.top = (document_nodes_start), \
+ (document).version_directive = (document_version_directive), \
+ (document).tag_directives.start = (document_tag_directives_start), \
+ (document).tag_directives.end = (document_tag_directives_end), \
+ (document).start_implicit = (document_start_implicit), \
+ (document).end_implicit = (document_end_implicit), \
+ (document).start_mark = (document_start_mark), \
+ (document).end_mark = (document_end_mark))
+
+/*
+ * Node initializers.
+ */
+
+#define NODE_INIT(node,node_type,node_tag,node_start_mark,node_end_mark) \
+ (memset(&(node), 0, sizeof(yaml_node_t)), \
+ (node).type = (node_type), \
+ (node).tag = (node_tag), \
+ (node).start_mark = (node_start_mark), \
+ (node).end_mark = (node_end_mark))
+
+#define SCALAR_NODE_INIT(node,node_tag,node_value,node_length, \
+ node_style,start_mark,end_mark) \
+ (NODE_INIT((node),YAML_SCALAR_NODE,(node_tag),(start_mark),(end_mark)), \
+ (node).data.scalar.value = (node_value), \
+ (node).data.scalar.length = (node_length), \
+ (node).data.scalar.style = (node_style))
+
+#define SEQUENCE_NODE_INIT(node,node_tag,node_items_start,node_items_end, \
+ node_style,start_mark,end_mark) \
+ (NODE_INIT((node),YAML_SEQUENCE_NODE,(node_tag),(start_mark),(end_mark)), \
+ (node).data.sequence.items.start = (node_items_start), \
+ (node).data.sequence.items.end = (node_items_end), \
+ (node).data.sequence.items.top = (node_items_start), \
+ (node).data.sequence.style = (node_style))
+
+#define MAPPING_NODE_INIT(node,node_tag,node_pairs_start,node_pairs_end, \
+ node_style,start_mark,end_mark) \
+ (NODE_INIT((node),YAML_MAPPING_NODE,(node_tag),(start_mark),(end_mark)), \
+ (node).data.mapping.pairs.start = (node_pairs_start), \
+ (node).data.mapping.pairs.end = (node_pairs_end), \
+ (node).data.mapping.pairs.top = (node_pairs_start), \
+ (node).data.mapping.style = (node_style))
+
diff --git a/lib/psych.rb b/lib/psych.rb
index 80fbfff..efcb142 100644
--- a/lib/psych.rb
+++ b/lib/psych.rb
@@ -20,14 +20,17 @@ require 'psych/stream'
require 'psych/json/tree_builder'
require 'psych/json/stream'
require 'psych/handlers/document_stream'
+require 'psych/class_loader'
###
# = Overview
#
-# Psych is a YAML parser and emitter. Psych leverages
-# libyaml[http://libyaml.org] for it's YAML parsing and emitting capabilities.
-# In addition to wrapping libyaml, Psych also knows how to serialize and
-# de-serialize most Ruby objects to and from the YAML format.
+# Psych is a YAML parser and emitter.
+# Psych leverages libyaml [Home page: http://pyyaml.org/wiki/LibYAML]
+# or [HG repo: https://bitbucket.org/xi/libyaml] for its YAML parsing
+# and emitting capabilities. In addition to wrapping libyaml, Psych also
+# knows how to serialize and de-serialize most Ruby objects to and from
+# the YAML format.
#
# = I NEED TO PARSE OR EMIT YAML RIGHT NOW!
#
@@ -45,16 +48,74 @@ require 'psych/handlers/document_stream'
# Psych provides a range of interfaces for parsing a YAML document ranging from
# low level to high level, depending on your parsing needs. At the lowest
# level, is an event based parser. Mid level is access to the raw YAML AST,
-# and at the highest level is the ability to unmarshal YAML to ruby objects.
+# and at the highest level is the ability to unmarshal YAML to Ruby objects.
#
-# === Low level parsing
+# == YAML Emitting
#
-# The lowest level parser should be used when the YAML input is already known,
-# and the developer does not want to pay the price of building an AST or
-# automatic detection and conversion to ruby objects. See Psych::Parser for
-# more information on using the event based parser.
+# Psych provides a range of interfaces ranging from low to high level for
+# producing YAML documents. Very similar to the YAML parsing interfaces, Psych
+# provides at the lowest level, an event based system, mid-level is building
+# a YAML AST, and the highest level is converting a Ruby object straight to
+# a YAML document.
+#
+# == High-level API
+#
+# === Parsing
+#
+# The high level YAML parser provided by Psych simply takes YAML as input and
+# returns a Ruby data structure. For information on using the high level parser
+# see Psych.load
+#
+# ==== Reading from a string
+#
+# Psych.load("--- a") # => 'a'
+# Psych.load("---\n - a\n - b") # => ['a', 'b']
+#
+# ==== Reading from a file
+#
+# Psych.load_file("database.yml")
+#
+# ==== Exception handling
+#
+# begin
+# # The second argument changes only the exception contents
+# Psych.parse("--- `", "file.txt")
+# rescue Psych::SyntaxError => ex
+# ex.file # => 'file.txt'
+# ex.message # => "(file.txt): found character that cannot start any token"
+# end
+#
+# === Emitting
+#
+# The high level emitter has the easiest interface. Psych simply takes a Ruby
+# data structure and converts it to a YAML document. See Psych.dump for more
+# information on dumping a Ruby data structure.
+#
+# ==== Writing to a string
+#
+# # Dump an array, get back a YAML string
+# Psych.dump(['a', 'b']) # => "---\n- a\n- b\n"
+#
+# # Dump an array to an IO object
+# Psych.dump(['a', 'b'], StringIO.new) # => #<StringIO:0x000001009d0890>
+#
+# # Dump an array with indentation set
+# Psych.dump(['a', ['b']], :indentation => 3) # => "---\n- a\n- - b\n"
+#
+# # Dump an array to an IO with indentation set
+# Psych.dump(['a', ['b']], StringIO.new, :indentation => 3)
+#
+# ==== Writing to a file
#
-# === Mid level parsing
+# Currently there is no direct API for dumping Ruby structure to file:
+#
+# File.open('database.yml', 'w') do |file|
+# file.write(Psych.dump(['a', 'b']))
+# end
+#
+# == Mid-level API
+#
+# === Parsing
#
# Psych provides access to an AST produced from parsing a YAML document. This
# tree is built using the Psych::Parser and Psych::TreeBuilder. The AST can
@@ -62,28 +123,33 @@ require 'psych/handlers/document_stream'
# Psych::Nodes, and Psych::Nodes::Node for more information on dealing with
# YAML syntax trees.
#
-# === High level parsing
+# ==== Reading from a string
#
-# The high level YAML parser provided by Psych simply takes YAML as input and
-# returns a Ruby data structure. For information on using the high level parser
-# see Psych.load
+# # Returns Psych::Nodes::Stream
+# Psych.parse_stream("---\n - a\n - b")
#
-# == YAML Emitting
+# # Returns Psych::Nodes::Document
+# Psych.parse("---\n - a\n - b")
#
-# Psych provides a range of interfaces ranging from low to high level for
-# producing YAML documents. Very similar to the YAML parsing interfaces, Psych
-# provides at the lowest level, an event based system, mid-level is building
-# a YAML AST, and the highest level is converting a Ruby object straight to
-# a YAML document.
+# ==== Reading from a file
#
-# === Low level emitting
+# # Returns Psych::Nodes::Stream
+# Psych.parse_stream(File.read('database.yml'))
#
-# The lowest level emitter is an event based system. Events are sent to a
-# Psych::Emitter object. That object knows how to convert the events to a YAML
-# document. This interface should be used when document format is known in
-# advance or speed is a concern. See Psych::Emitter for more information.
+# # Returns Psych::Nodes::Document
+# Psych.parse_file('database.yml')
+#
+# ==== Exception handling
+#
+# begin
+# # The second argument changes only the exception contents
+# Psych.parse("--- `", "file.txt")
+# rescue Psych::SyntaxError => ex
+# ex.file # => 'file.txt'
+# ex.message # => "(file.txt): found character that cannot start any token"
+# end
#
-# === Mid level emitting
+# === Emitting
#
# At the mid level is building an AST. This AST is exactly the same as the AST
# used when parsing a YAML document. Users can build an AST by hand and the
@@ -91,25 +157,77 @@ require 'psych/handlers/document_stream'
# Psych::Nodes::Node, and Psych::TreeBuilder for more information on building
# a YAML AST.
#
-# === High level emitting
+# ==== Writing to a string
#
-# The high level emitter has the easiest interface. Psych simply takes a Ruby
-# data structure and converts it to a YAML document. See Psych.dump for more
-# information on dumping a Ruby data structure.
+# # We need Psych::Nodes::Stream (not Psych::Nodes::Document)
+# stream = Psych.parse_stream("---\n - a\n - b")
+#
+# stream.to_yaml # => "---\n- a\n- b\n"
+#
+# ==== Writing to a file
+#
+# # We need Psych::Nodes::Stream (not Psych::Nodes::Document)
+# stream = Psych.parse_stream(File.read('database.yml'))
+#
+# File.open('database.yml', 'w') do |file|
+# file.write(stream.to_yaml)
+# end
+#
+# == Low-level API
+#
+# === Parsing
+#
+# The lowest level parser should be used when the YAML input is already known,
+# and the developer does not want to pay the price of building an AST or
+# automatic detection and conversion to Ruby objects. See Psych::Parser for
+# more information on using the event based parser.
+#
+# ==== Reading to Psych::Nodes::Stream structure
+#
+# parser = Psych::Parser.new(TreeBuilder.new) # => #<Psych::Parser>
+# parser = Psych.parser # it's an alias for the above
+#
+# parser.parse("---\n - a\n - b") # => #<Psych::Parser>
+# parser.handler # => #<Psych::TreeBuilder>
+# parser.handler.root # => #<Psych::Nodes::Stream>
+#
+# ==== Receiving an events stream
+#
+# parser = Psych::Parser.new(Psych::Handlers::Recorder.new)
+#
+# parser.parse("---\n - a\n - b")
+# parser.events # => [list of [event, args] lists]
+# # event is one of: Psych::Handler::EVENTS
+# # args are the arguments passed to the event
+#
+# === Emitting
+#
+# The lowest level emitter is an event based system. Events are sent to a
+# Psych::Emitter object. That object knows how to convert the events to a YAML
+# document. This interface should be used when document format is known in
+# advance or speed is a concern. See Psych::Emitter for more information.
+#
+# ==== Writing to a Ruby structure
+#
+# Psych.parser.parse("--- a") # => #<Psych::Parser>
+#
+# parser.handler.first # => #<Psych::Nodes::Stream>
+# parser.handler.first.to_ruby # => ["a"]
+#
+# parser.handler.root.first # => #<Psych::Nodes::Document>
+# parser.handler.root.first.to_ruby # => "a"
+#
+# # You can instantiate an Emitter manually
+# Psych::Visitors::ToRuby.new.accept(parser.handler.root.first)
+# # => "a"
module Psych
# The version is Psych you're using
- VERSION = '1.3.2'
+ VERSION = '2.0.9'
# The version of libyaml Psych is using
LIBYAML_VERSION = Psych.libyaml_version.join '.'
- class Exception < RuntimeError
- end
-
- class BadAlias < Exception
- end
-
###
# Load +yaml+ in to a Ruby data structure. If multiple documents are
# provided, the object contained in the first document will be returned.
@@ -127,7 +245,7 @@ module Psych
# Psych.load("--- `", "file.txt")
# rescue Psych::SyntaxError => ex
# ex.file # => 'file.txt'
- # ex.message # => "(foo.txt): found character that cannot start any token"
+ # ex.message # => "(file.txt): found character that cannot start any token"
# end
def self.load yaml, filename = nil
result = parse(yaml, filename)
@@ -135,7 +253,56 @@ module Psych
end
###
- # Parse a YAML string in +yaml+. Returns the first object of a YAML AST.
+ # Safely load the yaml string in +yaml+. By default, only the following
+ # classes are allowed to be deserialized:
+ #
+ # * TrueClass
+ # * FalseClass
+ # * NilClass
+ # * Numeric
+ # * String
+ # * Array
+ # * Hash
+ #
+ # Recursive data structures are not allowed by default. Arbitrary classes
+ # can be allowed by adding those classes to the +whitelist+. They are
+ # additive. For example, to allow Date deserialization:
+ #
+ # Psych.safe_load(yaml, [Date])
+ #
+ # Now the Date class can be loaded in addition to the classes listed above.
+ #
+ # Aliases can be explicitly allowed by changing the +aliases+ parameter.
+ # For example:
+ #
+ # x = []
+ # x << x
+ # yaml = Psych.dump x
+ # Psych.safe_load yaml # => raises an exception
+ # Psych.safe_load yaml, [], [], true # => loads the aliases
+ #
+ # A Psych::DisallowedClass exception will be raised if the yaml contains a
+ # class that isn't in the whitelist.
+ #
+ # A Psych::BadAlias exception will be raised if the yaml contains aliases
+ # but the +aliases+ parameter is set to false.
+ def self.safe_load yaml, whitelist_classes = [], whitelist_symbols = [], aliases = false, filename = nil
+ result = parse(yaml, filename)
+ return unless result
+
+ class_loader = ClassLoader::Restricted.new(whitelist_classes.map(&:to_s),
+ whitelist_symbols.map(&:to_s))
+ scanner = ScalarScanner.new class_loader
+ if aliases
+ visitor = Visitors::ToRuby.new scanner, class_loader
+ else
+ visitor = Visitors::NoAliasRuby.new scanner, class_loader
+ end
+ visitor.accept result
+ end
+
+ ###
+ # Parse a YAML string in +yaml+. Returns the Psych::Nodes::Document.
# +filename+ is used in the exception message if a Psych::SyntaxError is
# raised.
#
@@ -143,13 +310,13 @@ module Psych
#
# Example:
#
- # Psych.parse("---\n - a\n - b") # => #<Psych::Nodes::Sequence:0x00>
+ # Psych.parse("---\n - a\n - b") # => #<Psych::Nodes::Document:0x00>
#
# begin
# Psych.parse("--- `", "file.txt")
# rescue Psych::SyntaxError => ex
# ex.file # => 'file.txt'
- # ex.message # => "(foo.txt): found character that cannot start any token"
+ # ex.message # => "(file.txt): found character that cannot start any token"
# end
#
# See Psych::Nodes for more information about YAML AST.
@@ -161,7 +328,7 @@ module Psych
end
###
- # Parse a file at +filename+. Returns the YAML AST.
+ # Parse a file at +filename+. Returns the Psych::Nodes::Document.
#
# Raises a Psych::SyntaxError when a YAML syntax error is detected.
def self.parse_file filename
@@ -177,7 +344,7 @@ module Psych
end
###
- # Parse a YAML string in +yaml+. Returns the full AST for the YAML document.
+ # Parse a YAML string in +yaml+. Returns the Psych::Nodes::Stream.
# This method can handle multiple YAML documents contained in +yaml+.
# +filename+ is used in the exception message if a Psych::SyntaxError is
# raised.
@@ -199,7 +366,7 @@ module Psych
# Psych.parse_stream("--- `", "file.txt")
# rescue Psych::SyntaxError => ex
# ex.file # => 'file.txt'
- # ex.message # => "(foo.txt): found character that cannot start any token"
+ # ex.message # => "(file.txt): found character that cannot start any token"
# end
#
# See Psych::Nodes for more information about YAML AST.
@@ -244,7 +411,7 @@ module Psych
io = nil
end
- visitor = Psych::Visitors::YAMLTree.new options
+ visitor = Psych::Visitors::YAMLTree.create options
visitor << o
visitor.tree.yaml io, options
end
@@ -256,7 +423,7 @@ module Psych
#
# Psych.dump_stream("foo\n ", {}) # => "--- ! \"foo\\n \"\n--- {}\n"
def self.dump_stream *objects
- visitor = Psych::Visitors::YAMLTree.new {}
+ visitor = Psych::Visitors::YAMLTree.create({})
objects.each do |o|
visitor << o
end
@@ -264,16 +431,16 @@ module Psych
end
###
- # Dump Ruby object +o+ to a JSON string.
- def self.to_json o
- visitor = Psych::Visitors::JSONTree.new
- visitor << o
+ # Dump Ruby +object+ to a JSON string.
+ def self.to_json object
+ visitor = Psych::Visitors::JSONTree.create
+ visitor << object
visitor.tree.yaml
end
###
# Load multiple documents given in +yaml+. Returns the parsed documents
- # as a list. If a block is given, each document will be converted to ruby
+ # as a list. If a block is given, each document will be converted to Ruby
# and passed to the block during parsing
#
# Example:
@@ -298,7 +465,7 @@ module Psych
###
# Load the document contained in +filename+. Returns the yaml contained in
- # +filename+ as a ruby object
+ # +filename+ as a Ruby object
def self.load_file filename
File.open(filename, 'r:bom|utf-8') { |f| self.load f, filename }
end
@@ -324,7 +491,7 @@ module Psych
@load_tags = {}
@dump_tags = {}
def self.add_tag tag, klass
- @load_tags[tag] = klass
+ @load_tags[tag] = klass.name
@dump_tags[klass] = tag
end
diff --git a/lib/psych/class_loader.rb b/lib/psych/class_loader.rb
new file mode 100644
index 0000000..46c6b93
--- /dev/null
+++ b/lib/psych/class_loader.rb
@@ -0,0 +1,101 @@
+require 'psych/omap'
+require 'psych/set'
+
+module Psych
+ class ClassLoader # :nodoc:
+ BIG_DECIMAL = 'BigDecimal'
+ COMPLEX = 'Complex'
+ DATE = 'Date'
+ DATE_TIME = 'DateTime'
+ EXCEPTION = 'Exception'
+ OBJECT = 'Object'
+ PSYCH_OMAP = 'Psych::Omap'
+ PSYCH_SET = 'Psych::Set'
+ RANGE = 'Range'
+ RATIONAL = 'Rational'
+ REGEXP = 'Regexp'
+ STRUCT = 'Struct'
+ SYMBOL = 'Symbol'
+
+ def initialize
+ @cache = CACHE.dup
+ end
+
+ def load klassname
+ return nil if !klassname || klassname.empty?
+
+ find klassname
+ end
+
+ def symbolize sym
+ symbol
+ sym.to_sym
+ end
+
+ constants.each do |const|
+ konst = const_get const
+ define_method(const.to_s.downcase) do
+ load konst
+ end
+ end
+
+ private
+
+ def find klassname
+ @cache[klassname] ||= resolve(klassname)
+ end
+
+ def resolve klassname
+ name = klassname
+ retried = false
+
+ begin
+ path2class(name)
+ rescue ArgumentError, NameError => ex
+ unless retried
+ name = "Struct::#{name}"
+ retried = ex
+ retry
+ end
+ raise retried
+ end
+ end
+
+ CACHE = Hash[constants.map { |const|
+ val = const_get const
+ begin
+ [val, ::Object.const_get(val)]
+ rescue
+ nil
+ end
+ }.compact]
+
+ class Restricted < ClassLoader
+ def initialize classes, symbols
+ @classes = classes
+ @symbols = symbols
+ super()
+ end
+
+ def symbolize sym
+ return super if @symbols.empty?
+
+ if @symbols.include? sym
+ super
+ else
+ raise DisallowedClass, 'Symbol'
+ end
+ end
+
+ private
+
+ def find klassname
+ if @classes.include? klassname
+ super
+ else
+ raise DisallowedClass, klassname
+ end
+ end
+ end
+ end
+end
diff --git a/lib/psych/core_ext.rb b/lib/psych/core_ext.rb
index 4a04c2d..9c8134d 100644
--- a/lib/psych/core_ext.rb
+++ b/lib/psych/core_ext.rb
@@ -31,12 +31,5 @@ class Module
end
if defined?(::IRB)
-module Kernel
- def psych_y *objects
- puts Psych.dump_stream(*objects)
- end
- remove_method :y rescue nil
- alias y psych_y
- private :y
-end
+ require 'psych/y'
end
diff --git a/lib/psych/deprecated.rb b/lib/psych/deprecated.rb
index 333c3a1..8c310b3 100644
--- a/lib/psych/deprecated.rb
+++ b/lib/psych/deprecated.rb
@@ -21,6 +21,7 @@ module Psych
target.psych_to_yaml unless opts[:nodump]
end
+ # This method is deprecated, use Psych.load_stream instead.
def self.load_documents yaml, &block
if $VERBOSE
warn "#{caller[0]}: load_documents is deprecated, use load_stream"
@@ -34,7 +35,8 @@ module Psych
warn "#{caller[0]}: detect_implicit is deprecated" if $VERBOSE
return '' unless String === thing
return 'null' if '' == thing
- ScalarScanner.new.tokenize(thing).class.name.downcase
+ ss = ScalarScanner.new(ClassLoader.new)
+ ss.tokenize(thing).class.name.downcase
end
def self.add_ruby_type type_tag, &block
diff --git a/lib/psych/exception.rb b/lib/psych/exception.rb
new file mode 100644
index 0000000..ce9d2ca
--- /dev/null
+++ b/lib/psych/exception.rb
@@ -0,0 +1,13 @@
+module Psych
+ class Exception < RuntimeError
+ end
+
+ class BadAlias < Exception
+ end
+
+ class DisallowedClass < Exception
+ def initialize klass_name
+ super "Tried to load unspecified class: #{klass_name}"
+ end
+ end
+end
diff --git a/lib/psych/handler.rb b/lib/psych/handler.rb
index a2aa6bb..c55afe7 100644
--- a/lib/psych/handler.rb
+++ b/lib/psych/handler.rb
@@ -11,6 +11,34 @@ module Psych
# See Psych::Parser for more details
class Handler
###
+ # Configuration options for dumping YAML.
+ class DumperOptions
+ attr_accessor :line_width, :indentation, :canonical
+
+ def initialize
+ @line_width = 0
+ @indentation = 2
+ @canonical = false
+ end
+ end
+
+ # Default dumping options
+ OPTIONS = DumperOptions.new
+
+ # Events that a Handler should respond to.
+ EVENTS = [ :alias,
+ :empty,
+ :end_document,
+ :end_mapping,
+ :end_sequence,
+ :end_stream,
+ :scalar,
+ :start_document,
+ :start_mapping,
+ :start_sequence,
+ :start_stream ]
+
+ ###
# Called with +encoding+ when the YAML stream starts. This method is
# called once per stream. A stream may contain multiple documents.
#
diff --git a/lib/psych/handlers/recorder.rb b/lib/psych/handlers/recorder.rb
new file mode 100644
index 0000000..4eae62e
--- /dev/null
+++ b/lib/psych/handlers/recorder.rb
@@ -0,0 +1,39 @@
+require 'psych/handler'
+
+module Psych
+ module Handlers
+ ###
+ # This handler will capture an event and record the event. Recorder events
+ # are available vial Psych::Handlers::Recorder#events.
+ #
+ # For example:
+ #
+ # recorder = Psych::Handlers::Recorder.new
+ # parser = Psych::Parser.new recorder
+ # parser.parse '--- foo'
+ #
+ # recorder.events # => [list of events]
+ #
+ # # Replay the events
+ #
+ # emitter = Psych::Emitter.new $stdout
+ # recorder.events.each do |m, args|
+ # emitter.send m, *args
+ # end
+
+ class Recorder < Psych::Handler
+ attr_reader :events
+
+ def initialize
+ @events = []
+ super
+ end
+
+ EVENTS.each do |event|
+ define_method event do |*args|
+ @events << [event, args]
+ end
+ end
+ end
+ end
+end
diff --git a/lib/psych/json/stream.rb b/lib/psych/json/stream.rb
index be1a0a8..fe2a6e9 100644
--- a/lib/psych/json/stream.rb
+++ b/lib/psych/json/stream.rb
@@ -6,6 +6,7 @@ module Psych
class Stream < Psych::Visitors::JSONTree
include Psych::JSON::RubyEvents
include Psych::Streaming
+ extend Psych::Streaming::ClassMethods
class Emitter < Psych::Stream::Emitter # :nodoc:
include Psych::JSON::YAMLEvents
diff --git a/lib/psych/json/yaml_events.rb b/lib/psych/json/yaml_events.rb
index 01d4660..d054d9b 100644
--- a/lib/psych/json/yaml_events.rb
+++ b/lib/psych/json/yaml_events.rb
@@ -10,11 +10,11 @@ module Psych
end
def start_mapping anchor, tag, implicit, style
- super(anchor, nil, implicit, Nodes::Mapping::FLOW)
+ super(anchor, nil, true, Nodes::Mapping::FLOW)
end
def start_sequence anchor, tag, implicit, style
- super(anchor, nil, implicit, Nodes::Sequence::FLOW)
+ super(anchor, nil, true, Nodes::Sequence::FLOW)
end
def scalar value, anchor, tag, plain, quoted, style
diff --git a/lib/psych/nodes/node.rb b/lib/psych/nodes/node.rb
index 0cefe44..83233a6 100644
--- a/lib/psych/nodes/node.rb
+++ b/lib/psych/nodes/node.rb
@@ -1,4 +1,6 @@
require 'stringio'
+require 'psych/class_loader'
+require 'psych/scalar_scanner'
module Psych
module Nodes
@@ -32,7 +34,7 @@ module Psych
#
# See also Psych::Visitors::ToRuby
def to_ruby
- Visitors::ToRuby.new.accept self
+ Visitors::ToRuby.create.accept(self)
end
alias :transform :to_ruby
diff --git a/lib/psych/nodes/sequence.rb b/lib/psych/nodes/sequence.rb
index e4b833d..7e907fe 100644
--- a/lib/psych/nodes/sequence.rb
+++ b/lib/psych/nodes/sequence.rb
@@ -56,7 +56,7 @@ module Psych
# Is this sequence started implicitly?
attr_accessor :implicit
- # The sequece style used
+ # The sequence style used
attr_accessor :style
###
diff --git a/lib/psych/scalar_scanner.rb b/lib/psych/scalar_scanner.rb
index fa2d385..9300790 100644
--- a/lib/psych/scalar_scanner.rb
+++ b/lib/psych/scalar_scanner.rb
@@ -5,26 +5,39 @@ module Psych
# Scan scalars for built in types
class ScalarScanner
# Taken from http://yaml.org/type/timestamp.html
- TIME = /^\d{4}-\d{1,2}-\d{1,2}([Tt]|\s+)\d{1,2}:\d\d:\d\d(\.\d*)?(\s*Z|[-+]\d{1,2}(:\d\d)?)?/
+ TIME = /^-?\d{4}-\d{1,2}-\d{1,2}(?:[Tt]|\s+)\d{1,2}:\d\d:\d\d(?:\.\d*)?(?:\s*(?:Z|[-+]\d{1,2}:?(?:\d\d)?))?$/
# Taken from http://yaml.org/type/float.html
- FLOAT = /^(?:[-+]?([0-9][0-9_,]*)?\.[0-9.]*([eE][-+][0-9]+)?(?# base 10)
+ FLOAT = /^(?:[-+]?([0-9][0-9_,]*)?\.[0-9]*([eE][-+][0-9]+)?(?# base 10)
|[-+]?[0-9][0-9_,]*(:[0-5]?[0-9])+\.[0-9_]*(?# base 60)
|[-+]?\.(inf|Inf|INF)(?# infinity)
|\.(nan|NaN|NAN)(?# not a number))$/x
+ # Taken from http://yaml.org/type/int.html
+ INTEGER = /^(?:[-+]?0b[0-1_]+ (?# base 2)
+ |[-+]?0[0-7_]+ (?# base 8)
+ |[-+]?(?:0|[1-9][0-9_]*) (?# base 10)
+ |[-+]?0x[0-9a-fA-F_]+ (?# base 16))$/x
+
+ attr_reader :class_loader
+
# Create a new scanner
- def initialize
+ def initialize class_loader
@string_cache = {}
+ @symbol_cache = {}
+ @class_loader = class_loader
end
- # Tokenize +string+ returning the ruby object
+ # Tokenize +string+ returning the Ruby object
def tokenize string
return nil if string.empty?
return string if @string_cache.key?(string)
+ return @symbol_cache[string] if @symbol_cache.key?(string)
case string
- when /^[A-Za-z~]/
+ # Check for a String type, being careful not to get caught by hash keys, hex values, and
+ # special floats (e.g., -.inf).
+ when /^[^\d\.:-]?[A-Za-z_\s!@#\$%\^&\*\(\)\{\}\<\>\|\/\\~;=]+/, /\n/
if string.length > 5
@string_cache[string] = true
return string
@@ -45,25 +58,29 @@ module Psych
string
end
when TIME
- parse_time string
+ begin
+ parse_time string
+ rescue ArgumentError
+ string
+ end
when /^\d{4}-(?:1[012]|0\d|\d)-(?:[12]\d|3[01]|0\d|\d)$/
require 'date'
begin
- Date.strptime(string, '%Y-%m-%d')
+ class_loader.date.strptime(string, '%Y-%m-%d')
rescue ArgumentError
string
end
when /^\.inf$/i
- 1 / 0.0
+ Float::INFINITY
when /^-\.inf$/i
- -1 / 0.0
+ -Float::INFINITY
when /^\.nan$/i
- 0.0 / 0.0
+ Float::NAN
when /^:./
if string =~ /^:(["'])(.*)\1/
- $2.sub(/^:/, '').to_sym
+ @symbol_cache[string] = class_loader.symbolize($2.sub(/^:/, ''))
else
- string.sub(/^:/, '').to_sym
+ @symbol_cache[string] = class_loader.symbolize(string.sub(/^:/, ''))
end
when /^[-+]?[0-9][0-9_]*(:[0-5]?[0-9])+$/
i = 0
@@ -78,20 +95,15 @@ module Psych
end
i
when FLOAT
- begin
- return Float(string.gsub(/[,_]/, ''))
- rescue ArgumentError
+ if string =~ /\A[-+]?\.\Z/
+ @string_cache[string] = true
+ string
+ else
+ Float(string.gsub(/[,_]|\.$/, ''))
end
-
- @string_cache[string] = true
- string
else
- if string.count('.') < 2
- begin
- return Integer(string.gsub(/[,_]/, ''))
- rescue ArgumentError
- end
- end
+ int = parse_int string.gsub(/[,_]/, '')
+ return int if int
@string_cache[string] = true
string
@@ -99,19 +111,28 @@ module Psych
end
###
+ # Parse and return an int from +string+
+ def parse_int string
+ return unless INTEGER === string
+ Integer(string)
+ end
+
+ ###
# Parse and return a Time from +string+
def parse_time string
+ klass = class_loader.load 'Time'
+
date, time = *(string.split(/[ tT]/, 2))
- (yy, m, dd) = date.split('-').map { |x| x.to_i }
+ (yy, m, dd) = date.match(/^(-?\d{4})-(\d{1,2})-(\d{1,2})/).captures.map { |x| x.to_i }
md = time.match(/(\d+:\d+:\d+)(?:\.(\d*))?\s*(Z|[-+]\d+(:\d\d)?)?/)
(hh, mm, ss) = md[1].split(':').map { |x| x.to_i }
us = (md[2] ? Rational("0.#{md[2]}") : 0) * 1000000
- time = Time.utc(yy, m, dd, hh, mm, ss, us)
+ time = klass.utc(yy, m, dd, hh, mm, ss, us)
return time if 'Z' == md[3]
- return Time.at(time.to_i, us) unless md[3]
+ return klass.at(time.to_i, us) unless md[3]
tz = md[3].match(/^([+\-]?\d{1,2})\:?(\d{1,2})?$/)[1..-1].compact.map { |digit| Integer(digit, 10) }
offset = tz.first * 3600
@@ -122,7 +143,7 @@ module Psych
offset += ((tz[1] || 0) * 60)
end
- Time.at((time - offset).to_i, us)
+ klass.at((time - offset).to_i, us)
end
end
end
diff --git a/lib/psych/stream.rb b/lib/psych/stream.rb
index 567c1bb..88c4c4c 100644
--- a/lib/psych/stream.rb
+++ b/lib/psych/stream.rb
@@ -32,5 +32,6 @@ module Psych
end
include Psych::Streaming
+ extend Psych::Streaming::ClassMethods
end
end
diff --git a/lib/psych/streaming.rb b/lib/psych/streaming.rb
index c6fa109..9d94eb5 100644
--- a/lib/psych/streaming.rb
+++ b/lib/psych/streaming.rb
@@ -1,10 +1,15 @@
module Psych
module Streaming
- ###
- # Create a new streaming emitter. Emitter will print to +io+. See
- # Psych::Stream for an example.
- def initialize io
- super({}, self.class.const_get(:Emitter).new(io))
+ module ClassMethods
+ ###
+ # Create a new streaming emitter. Emitter will print to +io+. See
+ # Psych::Stream for an example.
+ def new io
+ emitter = const_get(:Emitter).new(io)
+ class_loader = ClassLoader.new
+ ss = ScalarScanner.new class_loader
+ super(emitter, ss, {})
+ end
end
###
diff --git a/lib/psych/syntax_error.rb b/lib/psych/syntax_error.rb
index f79743d..e200ef0 100644
--- a/lib/psych/syntax_error.rb
+++ b/lib/psych/syntax_error.rb
@@ -1,5 +1,7 @@
+require 'psych/exception'
+
module Psych
- class SyntaxError < ::SyntaxError
+ class SyntaxError < Psych::Exception
attr_reader :file, :line, :column, :offset, :problem, :context
def initialize file, line, col, offset, problem, context
diff --git a/lib/psych/visitors/emitter.rb b/lib/psych/visitors/emitter.rb
index 30db176..c886e50 100644
--- a/lib/psych/visitors/emitter.rb
+++ b/lib/psych/visitors/emitter.rb
@@ -2,10 +2,17 @@ module Psych
module Visitors
class Emitter < Psych::Visitors::Visitor
def initialize io, options = {}
- @handler = Psych::Emitter.new io
- @handler.indentation = options[:indentation] if options[:indentation]
- @handler.canonical = options[:canonical] if options[:canonical]
- @handler.line_width = options[:line_width] if options[:line_width]
+ opts = [:indentation, :canonical, :line_width].find_all { |opt|
+ options.key?(opt)
+ }
+
+ if opts.empty?
+ @handler = Psych::Emitter.new io
+ else
+ du = Handler::DumperOptions.new
+ opts.each { |option| du.send :"#{option}=", options[option] }
+ @handler = Psych::Emitter.new io, du
+ end
end
def visit_Psych_Nodes_Stream o
diff --git a/lib/psych/visitors/json_tree.rb b/lib/psych/visitors/json_tree.rb
index 0350dd1..0127ac8 100644
--- a/lib/psych/visitors/json_tree.rb
+++ b/lib/psych/visitors/json_tree.rb
@@ -5,8 +5,11 @@ module Psych
class JSONTree < YAMLTree
include Psych::JSON::RubyEvents
- def initialize options = {}, emitter = Psych::JSON::TreeBuilder.new
- super
+ def self.create options = {}
+ emitter = Psych::JSON::TreeBuilder.new
+ class_loader = ClassLoader.new
+ ss = ScalarScanner.new class_loader
+ new(emitter, ss, options)
end
def accept target
diff --git a/lib/psych/visitors/to_ruby.rb b/lib/psych/visitors/to_ruby.rb
index 2e082f9..f353e9c 100644
--- a/lib/psych/visitors/to_ruby.rb
+++ b/lib/psych/visitors/to_ruby.rb
@@ -1,4 +1,6 @@
require 'psych/scalar_scanner'
+require 'psych/class_loader'
+require 'psych/exception'
unless defined?(Regexp::NOENCODING)
Regexp::NOENCODING = 32
@@ -7,13 +9,22 @@ end
module Psych
module Visitors
###
- # This class walks a YAML AST, converting each node to ruby
+ # This class walks a YAML AST, converting each node to Ruby
class ToRuby < Psych::Visitors::Visitor
- def initialize ss = ScalarScanner.new
+ def self.create
+ class_loader = ClassLoader.new
+ scanner = ScalarScanner.new class_loader
+ new(scanner, class_loader)
+ end
+
+ attr_reader :class_loader
+
+ def initialize ss, class_loader
super()
@st = {}
@ss = ss
@domain_types = Psych.domain_types
+ @class_loader = class_loader
end
def accept target
@@ -32,7 +43,7 @@ module Psych
end
def deserialize o
- if klass = Psych.load_tags[o.tag]
+ if klass = resolve_class(Psych.load_tags[o.tag])
instance = klass.allocate
if instance.respond_to?(:init_with)
@@ -59,19 +70,25 @@ module Psych
end
when '!ruby/object:BigDecimal'
require 'bigdecimal'
- BigDecimal._load o.value
+ class_loader.big_decimal._load o.value
when "!ruby/object:DateTime"
+ class_loader.date_time
require 'date'
@ss.parse_time(o.value).to_datetime
+ when '!ruby/encoding'
+ ::Encoding.find o.value
when "!ruby/object:Complex"
+ class_loader.complex
Complex(o.value)
when "!ruby/object:Rational"
+ class_loader.rational
Rational(o.value)
when "!ruby/class", "!ruby/module"
resolve_class o.value
when "tag:yaml.org,2002:float", "!float"
Float(@ss.tokenize(o.value))
when "!ruby/regexp"
+ klass = class_loader.regexp
o.value =~ /^\/(.*)\/([mixn]*)$/
source = $1
options = 0
@@ -85,15 +102,16 @@ module Psych
else lang = option
end
end
- Regexp.new(*[source, options, lang].compact)
+ klass.new(*[source, options, lang].compact)
when "!ruby/range"
+ klass = class_loader.range
args = o.value.split(/([.]{2,3})/, 2).map { |s|
accept Nodes::Scalar.new(s)
}
args.push(args.delete_at(1) == '...')
- Range.new(*args)
+ klass.new(*args)
when /^!ruby\/sym(bol)?:?(.*)?$/
- o.value.to_sym
+ class_loader.symbolize o.value
else
@ss.tokenize o.value
end
@@ -105,7 +123,7 @@ module Psych
end
def visit_Psych_Nodes_Sequence o
- if klass = Psych.load_tags[o.tag]
+ if klass = resolve_class(Psych.load_tags[o.tag])
instance = klass.allocate
if instance.respond_to?(:init_with)
@@ -118,6 +136,8 @@ module Psych
end
case o.tag
+ when nil
+ register_empty(o)
when '!omap', 'tag:yaml.org,2002:omap'
map = register(o, Psych::Omap.new)
o.children.each { |a|
@@ -130,51 +150,29 @@ module Psych
o.children.each { |c| list.push accept c }
list
else
- list = register(o, [])
- o.children.each { |c| list.push accept c }
- list
+ register_empty(o)
end
end
def visit_Psych_Nodes_Mapping o
- return revive(Psych.load_tags[o.tag], o) if Psych.load_tags[o.tag]
- return revive_hash({}, o) unless o.tag
+ if Psych.load_tags[o.tag]
+ return revive(resolve_class(Psych.load_tags[o.tag]), o)
+ end
+ return revive_hash(register(o, {}), o) unless o.tag
case o.tag
- when /^!(?:str|ruby\/string)(?::(.*))?/, 'tag:yaml.org,2002:str'
- klass = resolve_class($1)
- members = Hash[*o.children.map { |c| accept c }]
- string = members.delete 'str'
-
- if klass
- string = klass.allocate
- string.replace string
- end
-
- init_with(string, members.map { |k,v| [k.to_s.sub(/^@/, ''),v] }, o)
- when /^!ruby\/array:(.*)$/
- klass = resolve_class($1)
- list = register(o, klass.allocate)
-
- members = Hash[o.children.map { |c| accept c }.each_slice(2).to_a]
- list.replace members['internal']
-
- members['ivars'].each do |ivar, v|
- list.instance_variable_set ivar, v
- end
- list
when /^!ruby\/struct:?(.*)?$/
- klass = resolve_class($1)
+ klass = resolve_class($1) if $1
if klass
s = register(o, klass.allocate)
members = {}
- struct_members = s.members.map { |x| x.to_sym }
+ struct_members = s.members.map { |x| class_loader.symbolize x }
o.children.each_slice(2) do |k,v|
member = accept(k)
value = accept(v)
- if struct_members.include?(member.to_sym)
+ if struct_members.include?(class_loader.symbolize(member))
s.send("#{member}=", value)
else
members[member.to_s.sub(/^@/, '')] = value
@@ -182,48 +180,128 @@ module Psych
end
init_with(s, members, o)
else
+ klass = class_loader.struct
members = o.children.map { |c| accept c }
h = Hash[*members]
- Struct.new(*h.map { |k,v| k.to_sym }).new(*h.map { |k,v| v })
+ s = klass.new(*h.map { |k,v|
+ class_loader.symbolize k
+ }).new(*h.map { |k,v| v })
+ register(o, s)
+ s
+ end
+
+ when /^!ruby\/object:?(.*)?$/
+ name = $1 || 'Object'
+
+ if name == 'Complex'
+ class_loader.complex
+ h = Hash[*o.children.map { |c| accept c }]
+ register o, Complex(h['real'], h['image'])
+ elsif name == 'Rational'
+ class_loader.rational
+ h = Hash[*o.children.map { |c| accept c }]
+ register o, Rational(h['numerator'], h['denominator'])
+ elsif name == 'Hash'
+ revive_hash(register(o, {}), o)
+ else
+ obj = revive((resolve_class(name) || class_loader.object), o)
+ obj
end
+ when /^!(?:str|ruby\/string)(?::(.*))?/, 'tag:yaml.org,2002:str'
+ klass = resolve_class($1)
+ members = {}
+ string = nil
+
+ o.children.each_slice(2) do |k,v|
+ key = accept k
+ value = accept v
+
+ if key == 'str'
+ if klass
+ string = klass.allocate.replace value
+ else
+ string = value
+ end
+ register(o, string)
+ else
+ members[key] = value
+ end
+ end
+ init_with(string, members.map { |k,v| [k.to_s.sub(/^@/, ''),v] }, o)
+ when /^!ruby\/array:(.*)$/
+ klass = resolve_class($1)
+ list = register(o, klass.allocate)
+
+ members = Hash[o.children.map { |c| accept c }.each_slice(2).to_a]
+ list.replace members['internal']
+
+ members['ivars'].each do |ivar, v|
+ list.instance_variable_set ivar, v
+ end
+ list
+
when '!ruby/range'
+ klass = class_loader.range
h = Hash[*o.children.map { |c| accept c }]
- register o, Range.new(h['begin'], h['end'], h['excl'])
+ register o, klass.new(h['begin'], h['end'], h['excl'])
when /^!ruby\/exception:?(.*)?$/
h = Hash[*o.children.map { |c| accept c }]
- e = build_exception((resolve_class($1) || Exception),
+ e = build_exception((resolve_class($1) || class_loader.exception),
h.delete('message'))
init_with(e, h, o)
when '!set', 'tag:yaml.org,2002:set'
- set = Psych::Set.new
+ set = class_loader.psych_set.new
@st[o.anchor] = set if o.anchor
o.children.each_slice(2) do |k,v|
set[accept(k)] = accept(v)
end
set
- when '!ruby/object:Complex'
- h = Hash[*o.children.map { |c| accept c }]
- register o, Complex(h['real'], h['image'])
+ when /^!ruby\/hash-with-ivars(?::(.*))?$/
+ hash = $1 ? resolve_class($1).allocate : {}
+ o.children.each_slice(2) do |key, value|
+ case key.value
+ when 'elements'
+ revive_hash hash, value
+ when 'ivars'
+ value.children.each_slice(2) do |k,v|
+ hash.instance_variable_set accept(k), accept(v)
+ end
+ end
+ end
+ hash
- when '!ruby/object:Rational'
- h = Hash[*o.children.map { |c| accept c }]
- register o, Rational(h['numerator'], h['denominator'])
+ when /^!map:(.*)$/, /^!ruby\/hash:(.*)$/
+ revive_hash register(o, resolve_class($1).allocate), o
- when /^!ruby\/object:?(.*)?$/
- name = $1 || 'Object'
- obj = revive((resolve_class(name) || Object), o)
- obj
+ when '!omap', 'tag:yaml.org,2002:omap'
+ map = register(o, class_loader.psych_omap.new)
+ o.children.each_slice(2) do |l,r|
+ map[accept(l)] = accept r
+ end
+ map
- when /^!map:(.*)$/, /^!ruby\/hash:(.*)$/
- revive_hash resolve_class($1).new, o
+ when /^!ruby\/marshalable:(.*)$/
+ name = $1
+ klass = resolve_class(name)
+ obj = register(o, klass.allocate)
+
+ if obj.respond_to?(:init_with)
+ init_with(obj, revive_hash({}, o), o)
+ elsif obj.respond_to?(:marshal_load)
+ marshal_data = o.children.map(&method(:accept))
+ obj.marshal_load(marshal_data)
+ obj
+ else
+ raise ArgumentError, "Cannot deserialize #{name}"
+ end
else
- revive_hash({}, o)
+ revive_hash(register(o, {}), o)
end
end
@@ -245,36 +323,52 @@ module Psych
object
end
- def revive_hash hash, o
- @st[o.anchor] = hash if o.anchor
+ def register_empty object
+ list = register(object, [])
+ object.children.each { |c| list.push accept c }
+ list
+ end
- o.children.each_slice(2) { |k,v|
+ def revive_hash hash, o
+ o.children.each_slice(2) { |k,v|
key = accept(k)
+ val = accept(v)
- if key == '<<'
+ if key == '<<' && k.tag != "tag:yaml.org,2002:str"
case v
- when Nodes::Alias
- hash.merge! accept(v)
+ when Nodes::Alias, Nodes::Mapping
+ begin
+ hash.merge! val
+ rescue TypeError
+ hash[key] = val
+ end
when Nodes::Sequence
- accept(v).reverse_each do |value|
- hash.merge! value
+ begin
+ h = {}
+ val.reverse_each do |value|
+ h.merge! value
+ end
+ hash.merge! h
+ rescue TypeError
+ hash[key] = val
end
else
- hash[key] = accept(v)
+ hash[key] = val
end
else
- hash[key] = accept(v)
+ hash[key] = val
end
}
hash
end
+ def merge_key hash, key, val
+ end
+
def revive klass, node
- s = klass.allocate
- @st[node.anchor] = s if node.anchor
- h = Hash[*node.children.map { |c| accept c }]
- init_with(s, h, node)
+ s = register(node, klass.allocate)
+ init_with(s, revive_hash({}, node), node)
end
def init_with o, h, node
@@ -296,21 +390,13 @@ module Psych
# Convert +klassname+ to a Class
def resolve_class klassname
- return nil unless klassname and not klassname.empty?
-
- name = klassname
- retried = false
-
- begin
- path2class(name)
- rescue ArgumentError, NameError => ex
- unless retried
- name = "Struct::#{name}"
- retried = ex
- retry
- end
- raise retried
- end
+ class_loader.load klassname
+ end
+ end
+
+ class NoAliasRuby < ToRuby
+ def visit_Psych_Nodes_Alias o
+ raise BadAlias, "Unknown alias: #{o.anchor}"
end
end
end
diff --git a/lib/psych/visitors/yaml_tree.rb b/lib/psych/visitors/yaml_tree.rb
index 80af046..3a6bd1d 100644
--- a/lib/psych/visitors/yaml_tree.rb
+++ b/lib/psych/visitors/yaml_tree.rb
@@ -1,25 +1,74 @@
+require 'psych/tree_builder'
+require 'psych/scalar_scanner'
+require 'psych/class_loader'
+
module Psych
module Visitors
###
- # YAMLTree builds a YAML ast given a ruby object. For example:
+ # YAMLTree builds a YAML ast given a Ruby object. For example:
#
# builder = Psych::Visitors::YAMLTree.new
# builder << { :foo => 'bar' }
# builder.tree # => #<Psych::Nodes::Stream .. }
#
class YAMLTree < Psych::Visitors::Visitor
+ class Registrar # :nodoc:
+ def initialize
+ @obj_to_id = {}
+ @obj_to_node = {}
+ @targets = []
+ @counter = 0
+ end
+
+ def register target, node
+ @targets << target
+ @obj_to_node[target.object_id] = node
+ end
+
+ def key? target
+ @obj_to_node.key? target.object_id
+ rescue NoMethodError
+ false
+ end
+
+ def id_for target
+ @obj_to_id[target.object_id] ||= (@counter += 1)
+ end
+
+ def node_for target
+ @obj_to_node[target.object_id]
+ end
+ end
+
attr_reader :started, :finished
alias :finished? :finished
alias :started? :started
- def initialize options = {}, emitter = TreeBuilder.new, ss = ScalarScanner.new
+ def self.create options = {}, emitter = nil
+ emitter ||= TreeBuilder.new
+ class_loader = ClassLoader.new
+ ss = ScalarScanner.new class_loader
+ new(emitter, ss, options)
+ end
+
+ def self.new emitter = nil, ss = nil, options = nil
+ return super if emitter && ss && options
+
+ if $VERBOSE
+ warn "This API is deprecated, please pass an emitter, scalar scanner, and options or call #{self}.create() (#{caller.first})"
+ end
+ create emitter, ss
+ end
+
+ def initialize emitter, ss, options
super()
@started = false
@finished = false
@emitter = emitter
- @st = {}
+ @st = Registrar.new
@ss = ss
@options = options
+ @coders = []
@dispatch_cache = Hash.new do |h,klass|
method = "visit_#{(klass.name || '').split('::').join('_')}"
@@ -46,6 +95,7 @@ module Psych
def tree
finish unless finished?
+ @emitter.root
end
def push object
@@ -64,15 +114,15 @@ module Psych
@emitter.start_document version, [], false
accept object
- @emitter.end_document
+ @emitter.end_document !@emitter.streaming?
end
alias :<< :push
def accept target
# return any aliases we find
- if @st.key? target.object_id
- oid = target.object_id
- node = @st[oid]
+ if @st.key? target
+ oid = @st.id_for target
+ node = @st.node_for target
anchor = oid.to_s
node.anchor = anchor
return @emitter.alias anchor
@@ -111,6 +161,11 @@ module Psych
@emitter.end_sequence
end
+ def visit_Encoding o
+ tag = "!ruby/encoding"
+ @emitter.scalar o.name, nil, tag, false, false, Nodes::Scalar::ANY
+ end
+
def visit_Object o
tag = Psych.dump_tags[o.class]
unless tag
@@ -158,19 +213,42 @@ module Psych
@emitter.end_mapping
end
+ def visit_NameError o
+ tag = ['!ruby/exception', o.class.name].join ':'
+
+ @emitter.start_mapping nil, tag, false, Nodes::Mapping::BLOCK
+
+ {
+ 'message' => o.message.to_s,
+ 'backtrace' => private_iv_get(o, 'backtrace'),
+ }.each do |k,v|
+ next unless v
+ @emitter.scalar k, nil, nil, true, false, Nodes::Scalar::ANY
+ accept v
+ end
+
+ dump_ivars o
+
+ @emitter.end_mapping
+ end
+
def visit_Regexp o
register o, @emitter.scalar(o.inspect, nil, '!ruby/regexp', false, false, Nodes::Scalar::ANY)
end
def visit_DateTime o
- formatted = format_time o.to_time
+ formatted = if o.offset.zero?
+ o.strftime("%Y-%m-%d %H:%M:%S.%9N Z".freeze)
+ else
+ o.strftime("%Y-%m-%d %H:%M:%S.%9N %:z".freeze)
+ end
tag = '!ruby/object:DateTime'
register o, @emitter.scalar(formatted, nil, tag, false, false, Nodes::Scalar::ANY)
end
def visit_Time o
formatted = format_time o
- @emitter.scalar formatted, nil, nil, true, false, Nodes::Scalar::ANY
+ register o, @emitter.scalar(formatted, nil, nil, true, false, Nodes::Scalar::ANY)
end
def visit_Rational o
@@ -218,28 +296,33 @@ module Psych
@emitter.scalar o._dump, nil, '!ruby/object:BigDecimal', false, false, Nodes::Scalar::ANY
end
- def binary? string
- string.encoding == Encoding::ASCII_8BIT ||
- string.index("\x00") ||
- string.count("\x00-\x7F", "^ -~\t\r\n").fdiv(string.length) > 0.3
- end
- private :binary?
-
def visit_String o
- plain = false
- quote = false
- style = Nodes::Scalar::ANY
+ plain = true
+ quote = true
+ style = Nodes::Scalar::PLAIN
+ tag = nil
+ str = o
if binary?(o)
str = [o].pack('m').chomp
tag = '!binary' # FIXME: change to below when syck is removed
#tag = 'tag:yaml.org,2002:binary'
style = Nodes::Scalar::LITERAL
+ plain = false
+ quote = false
+ elsif o =~ /\n/
+ style = Nodes::Scalar::LITERAL
+ elsif o == '<<'
+ style = Nodes::Scalar::SINGLE_QUOTED
+ tag = 'tag:yaml.org,2002:str'
+ plain = false
+ quote = false
+ elsif o =~ /^[^[:word:]][^"]*$/
+ style = Nodes::Scalar::DOUBLE_QUOTED
else
- str = o
- tag = nil
- quote = !(String === @ss.tokenize(o))
- plain = !quote
+ unless String === @ss.tokenize(o)
+ style = Nodes::Scalar::SINGLE_QUOTED
+ end
end
ivars = find_ivars o
@@ -247,13 +330,15 @@ module Psych
if ivars.empty?
unless o.class == ::String
tag = "!ruby/string:#{o.class}"
+ plain = false
+ quote = false
end
@emitter.scalar str, nil, tag, plain, quote, style
else
maptag = '!ruby/string'
maptag << ":#{o.class}" unless o.class == ::String
- @emitter.start_mapping nil, maptag, false, Nodes::Mapping::BLOCK
+ register o, @emitter.start_mapping(nil, maptag, false, Nodes::Mapping::BLOCK)
@emitter.scalar 'str', nil, nil, true, false, Nodes::Scalar::ANY
@emitter.scalar str, nil, tag, plain, quote, style
@@ -282,17 +367,46 @@ module Psych
end
def visit_Hash o
- tag = o.class == ::Hash ? nil : "!ruby/hash:#{o.class}"
- implicit = !tag
+ ivars = o.instance_variables
- register(o, @emitter.start_mapping(nil, tag, implicit, Psych::Nodes::Mapping::BLOCK))
+ if ivars.any?
+ tag = "!ruby/hash-with-ivars"
+ tag << ":#{o.class}" unless o.class == ::Hash
- o.each do |k,v|
- accept k
- accept v
- end
+ register(o, @emitter.start_mapping(nil, tag, false, Psych::Nodes::Mapping::BLOCK))
- @emitter.end_mapping
+ @emitter.scalar 'elements', nil, nil, true, false, Nodes::Scalar::ANY
+
+ @emitter.start_mapping nil, nil, true, Nodes::Mapping::BLOCK
+ o.each do |k,v|
+ accept k
+ accept v
+ end
+ @emitter.end_mapping
+
+ @emitter.scalar 'ivars', nil, nil, true, false, Nodes::Scalar::ANY
+
+ @emitter.start_mapping nil, nil, true, Nodes::Mapping::BLOCK
+ o.instance_variables.each do |ivar|
+ accept ivar
+ accept o.instance_variable_get ivar
+ end
+ @emitter.end_mapping
+
+ @emitter.end_mapping
+ else
+ tag = o.class == ::Hash ? nil : "!ruby/hash:#{o.class}"
+ implicit = !tag
+
+ register(o, @emitter.start_mapping(nil, tag, implicit, Psych::Nodes::Mapping::BLOCK))
+
+ o.each do |k,v|
+ accept k
+ accept v
+ end
+
+ @emitter.end_mapping
+ end
end
def visit_Psych_Set o
@@ -321,10 +435,37 @@ module Psych
end
def visit_Symbol o
- @emitter.scalar ":#{o}", nil, nil, true, false, Nodes::Scalar::ANY
+ if o.empty?
+ @emitter.scalar "", nil, '!ruby/symbol', false, false, Nodes::Scalar::ANY
+ else
+ @emitter.scalar ":#{o}", nil, nil, true, false, Nodes::Scalar::ANY
+ end
+ end
+
+ def visit_BasicObject o
+ tag = Psych.dump_tags[o.class]
+ tag ||= "!ruby/marshalable:#{o.class.name}"
+
+ map = @emitter.start_mapping(nil, tag, false, Nodes::Mapping::BLOCK)
+ register(o, map)
+
+ o.marshal_dump.each(&method(:accept))
+
+ @emitter.end_mapping
end
private
+ # FIXME: Remove the index and count checks in Psych 3.0
+ NULL = "\x00"
+ BINARY_RANGE = "\x00-\x7F"
+ WS_RANGE = "^ -~\t\r\n"
+
+ def binary? string
+ (string.encoding == Encoding::ASCII_8BIT && !string.ascii_only?) ||
+ string.index(NULL) ||
+ string.count(BINARY_RANGE, WS_RANGE).fdiv(string.length) > 0.3
+ end
+
def visit_array_subclass o
tag = "!ruby/array:#{o.class}"
if o.instance_variables.empty?
@@ -401,11 +542,12 @@ module Psych
end
def register target, yaml_obj
- @st[target.object_id] = yaml_obj
+ @st.register target, yaml_obj
yaml_obj
end
def dump_coder o
+ @coders << o
tag = Psych.dump_tags[o.class]
unless tag
klass = o.class == Object ? nil : o.class.name
@@ -430,7 +572,7 @@ module Psych
when :map
@emitter.start_mapping nil, c.tag, c.implicit, c.style
c.map.each do |k,v|
- @emitter.scalar k, nil, nil, true, false, Nodes::Scalar::ANY
+ accept k
accept v
end
@emitter.end_mapping
diff --git a/lib/psych/y.rb b/lib/psych/y.rb
new file mode 100644
index 0000000..d0e049d
--- /dev/null
+++ b/lib/psych/y.rb
@@ -0,0 +1,9 @@
+module Kernel
+ ###
+ # An alias for Psych.dump_stream meant to be used with IRB.
+ def y *objects
+ puts Psych.dump_stream(*objects)
+ end
+ private :y
+end
+
diff --git a/test/psych/.swp b/test/psych/.swp
deleted file mode 100644
index ea9da4d..0000000
--- a/test/psych/.swp
+++ /dev/null
Binary files differ
diff --git a/test/psych/handlers/test_recorder.rb b/test/psych/handlers/test_recorder.rb
new file mode 100644
index 0000000..96b8eac
--- /dev/null
+++ b/test/psych/handlers/test_recorder.rb
@@ -0,0 +1,25 @@
+require 'psych/helper'
+require 'psych/handlers/recorder'
+
+module Psych
+ module Handlers
+ class TestRecorder < TestCase
+ def test_replay
+ yaml = "--- foo\n...\n"
+ output = StringIO.new
+
+ recorder = Psych::Handlers::Recorder.new
+ parser = Psych::Parser.new recorder
+ parser.parse yaml
+
+ assert_equal 5, recorder.events.length
+
+ emitter = Psych::Emitter.new output
+ recorder.events.each do |m, args|
+ emitter.send m, *args
+ end
+ assert_equal yaml, output.string
+ end
+ end
+ end
+end
diff --git a/test/psych/helper.rb b/test/psych/helper.rb
index 1c57556..468beac 100644
--- a/test/psych/helper.rb
+++ b/test/psych/helper.rb
@@ -7,6 +7,35 @@ require 'psych'
module Psych
class TestCase < MiniTest::Unit::TestCase
+ def self.suppress_warning
+ verbose, $VERBOSE = $VERBOSE, nil
+ yield
+ ensure
+ $VERBOSE = verbose
+ end
+
+ def with_default_external(enc)
+ verbose, $VERBOSE = $VERBOSE, nil
+ origenc, Encoding.default_external = Encoding.default_external, enc
+ $VERBOSE = verbose
+ yield
+ ensure
+ verbose, $VERBOSE = $VERBOSE, nil
+ Encoding.default_external = origenc
+ $VERBOSE = verbose
+ end
+
+ def with_default_internal(enc)
+ verbose, $VERBOSE = $VERBOSE, nil
+ origenc, Encoding.default_internal = Encoding.default_internal, enc
+ $VERBOSE = verbose
+ yield
+ ensure
+ verbose, $VERBOSE = $VERBOSE, nil
+ Encoding.default_internal = origenc
+ $VERBOSE = verbose
+ end
+
#
# Convert between Psych and the object to verify correct parsing and
# emitting
@@ -32,7 +61,7 @@ module Psych
end
def assert_cycle( obj )
- v = Visitors::YAMLTree.new
+ v = Visitors::YAMLTree.create
v << obj
assert_equal(obj, Psych.load(v.tree.yaml))
assert_equal( obj, Psych::load(Psych.dump(obj)))
@@ -55,3 +84,32 @@ module Psych
end
end
end
+
+# backport so that tests will run on 1.9 and 2.0.0
+unless Tempfile.respond_to? :create
+ def Tempfile.create(basename, *rest)
+ tmpfile = nil
+ Dir::Tmpname.create(basename, *rest) do |tmpname, n, opts|
+ mode = File::RDWR|File::CREAT|File::EXCL
+ perm = 0600
+ if opts
+ mode |= opts.delete(:mode) || 0
+ opts[:perm] = perm
+ perm = nil
+ else
+ opts = perm
+ end
+ tmpfile = File.open(tmpname, mode, opts)
+ end
+ if block_given?
+ begin
+ yield tmpfile
+ ensure
+ tmpfile.close if !tmpfile.closed?
+ File.unlink tmpfile
+ end
+ else
+ tmpfile
+ end
+ end
+end
diff --git a/test/psych/json/test_stream.rb b/test/psych/json/test_stream.rb
index 4690ad2..b0c33e6 100644
--- a/test/psych/json/test_stream.rb
+++ b/test/psych/json/test_stream.rb
@@ -65,7 +65,7 @@ module Psych
@stream.push list
json = @io.string
- assert_match(/]$/, json)
+ assert_match(/\]$/, json)
assert_match(/^--- \[/, json)
assert_match(/["]one["]/, json)
assert_match(/["]two["]/, json)
diff --git a/test/psych/test_alias_and_anchor.rb b/test/psych/test_alias_and_anchor.rb
index 48771d6..9e2c240 100644
--- a/test/psych/test_alias_and_anchor.rb
+++ b/test/psych/test_alias_and_anchor.rb
@@ -1,4 +1,12 @@
-require 'psych/helper'
+require_relative 'helper'
+
+class ObjectWithInstanceVariables
+ attr_accessor :var1, :var2
+end
+
+class SubStringWithInstanceVariables < String
+ attr_accessor :var1
+end
module Psych
class TestAliasAndAnchor < TestCase
@@ -14,6 +22,40 @@ EOYAML
result.each {|el| assert_same(result[0], el) }
end
+ def test_mri_compatibility_object_with_ivars
+ yaml = <<EOYAML
+---
+- &id001 !ruby/object:ObjectWithInstanceVariables
+ var1: test1
+ var2: test2
+- *id001
+- *id001
+EOYAML
+
+ result = Psych.load yaml
+ result.each do |el|
+ assert_same(result[0], el)
+ assert_equal('test1', el.var1)
+ assert_equal('test2', el.var2)
+ end
+ end
+
+ def test_mri_compatibility_substring_with_ivars
+ yaml = <<EOYAML
+---
+- &id001 !str:SubStringWithInstanceVariables
+ str: test
+ "@var1": test
+- *id001
+- *id001
+EOYAML
+ result = Psych.load yaml
+ result.each do |el|
+ assert_same(result[0], el)
+ assert_equal('test', el.var1)
+ end
+ end
+
def test_anchor_alias_round_trip
o = Object.new
original = [o,o,o]
@@ -22,5 +64,33 @@ EOYAML
result = Psych.load yaml
result.each {|el| assert_same(result[0], el) }
end
+
+ def test_anchor_alias_round_trip_object_with_ivars
+ o = ObjectWithInstanceVariables.new
+ o.var1 = 'test1'
+ o.var2 = 'test2'
+ original = [o,o,o]
+
+ yaml = Psych.dump original
+ result = Psych.load yaml
+ result.each do |el|
+ assert_same(result[0], el)
+ assert_equal('test1', el.var1)
+ assert_equal('test2', el.var2)
+ end
+ end
+
+ def test_anchor_alias_round_trip_substring_with_ivars
+ o = SubStringWithInstanceVariables.new
+ o.var1 = 'test'
+ original = [o,o,o]
+
+ yaml = Psych.dump original
+ result = Psych.load yaml
+ result.each do |el|
+ assert_same(result[0], el)
+ assert_equal('test', el.var1)
+ end
+ end
end
end
diff --git a/test/psych/test_array.rb b/test/psych/test_array.rb
index 9eedbb4..960ffd7 100644
--- a/test/psych/test_array.rb
+++ b/test/psych/test_array.rb
@@ -1,4 +1,4 @@
-require 'psych/helper'
+require_relative 'helper'
module Psych
class TestArray < TestCase
@@ -14,6 +14,16 @@ module Psych
@list = [{ :a => 'b' }, 'foo']
end
+ def test_another_subclass_with_attributes
+ y = Y.new.tap {|y| y.val = 1}
+ y << "foo" << "bar"
+ y = Psych.load Psych.dump y
+
+ assert_equal %w{foo bar}, y
+ assert_equal Y, y.class
+ assert_equal 1, y.val
+ end
+
def test_subclass
yaml = Psych.dump X.new
assert_match X.name, yaml
diff --git a/test/psych/test_boolean.rb b/test/psych/test_boolean.rb
index ebfa25e..b656f4f 100644
--- a/test/psych/test_boolean.rb
+++ b/test/psych/test_boolean.rb
@@ -1,4 +1,4 @@
-require 'psych/helper'
+require_relative 'helper'
module Psych
###
diff --git a/test/psych/test_class.rb b/test/psych/test_class.rb
index 156f2fb..c7f964c 100644
--- a/test/psych/test_class.rb
+++ b/test/psych/test_class.rb
@@ -1,4 +1,4 @@
-require 'psych/helper'
+require_relative 'helper'
module Psych
class TestClass < TestCase
diff --git a/test/psych/test_coder.rb b/test/psych/test_coder.rb
index 7539c7d..7571e89 100644
--- a/test/psych/test_coder.rb
+++ b/test/psych/test_coder.rb
@@ -1,4 +1,4 @@
-require 'psych/helper'
+require_relative 'helper'
module Psych
class TestCoder < TestCase
@@ -85,7 +85,7 @@ module Psych
end
def encode_with coder
- coder.represent_map self.class.name, { 'a' => 'b' }
+ coder.represent_map self.class.name, { "string" => 'a', :symbol => 'b' }
end
end
@@ -131,7 +131,7 @@ module Psych
def test_represent_map
thing = Psych.load(Psych.dump(RepresentWithMap.new))
- assert_equal({ 'a' => 'b' }, thing.map)
+ assert_equal({ "string" => 'a', :symbol => 'b' }, thing.map)
end
def test_represent_sequence
diff --git a/test/psych/test_date_time.rb b/test/psych/test_date_time.rb
index fb76cb3..72150ad 100644
--- a/test/psych/test_date_time.rb
+++ b/test/psych/test_date_time.rb
@@ -1,8 +1,21 @@
-require 'psych/helper'
+require_relative 'helper'
require 'date'
module Psych
class TestDateTime < TestCase
+ def test_negative_year
+ time = Time.utc -1, 12, 16
+ assert_cycle time
+ end
+
+ def test_new_datetime
+ assert_cycle DateTime.new
+ end
+
+ def test_invalid_date
+ assert_cycle "2013-10-31T10:40:07-000000000000033"
+ end
+
def test_string_tag
dt = DateTime.now
yaml = Psych.dump dt
@@ -13,5 +26,13 @@ module Psych
dt = DateTime.now
assert_cycle dt
end
+
+ def test_alias_with_time
+ t = Time.now
+ h = {:a => t, :b => t}
+ yaml = Psych.dump h
+ assert_match('&', yaml)
+ assert_match('*', yaml)
+ end
end
end
diff --git a/test/psych/test_deprecated.rb b/test/psych/test_deprecated.rb
index 3740b6f..fd2d329 100644
--- a/test/psych/test_deprecated.rb
+++ b/test/psych/test_deprecated.rb
@@ -1,8 +1,9 @@
-require 'psych/helper'
+require_relative 'helper'
module Psych
class TestDeprecated < TestCase
def teardown
+ $VERBOSE = @orig_verbose
Psych.domain_types.clear
end
@@ -27,6 +28,7 @@ module Psych
def setup
@qe = QuickEmitter.new
+ @orig_verbose, $VERBOSE = $VERBOSE, false
end
def test_quick_emit
@@ -145,7 +147,9 @@ module Psych
end
class YamlAs
- psych_yaml_as 'helloworld' # this should be yaml_as but to avoid syck
+ TestCase.suppress_warning do
+ psych_yaml_as 'helloworld' # this should be yaml_as but to avoid syck
+ end
end
def test_yaml_as
diff --git a/test/psych/test_document.rb b/test/psych/test_document.rb
index 05d9bbf..bd77d60 100644
--- a/test/psych/test_document.rb
+++ b/test/psych/test_document.rb
@@ -1,4 +1,4 @@
-require 'psych/helper'
+require_relative 'helper'
module Psych
class TestDocument < TestCase
diff --git a/test/psych/test_emitter.rb b/test/psych/test_emitter.rb
index dfd20e1..1c96c12 100644
--- a/test/psych/test_emitter.rb
+++ b/test/psych/test_emitter.rb
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-require 'psych/helper'
+require_relative 'helper'
module Psych
class TestEmitter < TestCase
@@ -11,8 +11,7 @@ module Psych
end
def test_line_width
- assert_equal 0, @emitter.line_width
- assert_equal 10, @emitter.line_width = 10
+ @emitter.line_width = 10
assert_equal 10, @emitter.line_width
end
diff --git a/test/psych/test_encoding.rb b/test/psych/test_encoding.rb
index 1abee0d..517cae2 100644
--- a/test/psych/test_encoding.rb
+++ b/test/psych/test_encoding.rb
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-require 'psych/helper'
+require_relative 'helper'
module Psych
class TestEncoding < TestCase
@@ -31,6 +31,11 @@ module Psych
@emitter = Psych::Emitter.new @buffer
end
+ def test_dump_load_encoding_object
+ assert_cycle Encoding::US_ASCII
+ assert_cycle Encoding::UTF_8
+ end
+
def test_transcode_shiftjis
str = "こんにちは!"
loaded = Psych.load("--- こんにちは!".encode('SHIFT_JIS'))
@@ -50,58 +55,54 @@ module Psych
end
def test_io_shiftjis
- t = Tempfile.new(['shiftjis', 'yml'], :encoding => 'SHIFT_JIS')
- t.write '--- こんにちは!'
- t.close
-
- # If the external encoding isn't utf8, utf16le, or utf16be, we cannot
- # process the file.
- File.open(t.path, 'r', :encoding => 'SHIFT_JIS') do |f|
- assert_raises Psych::SyntaxError do
- Psych.load(f)
+ Tempfile.create(['shiftjis', 'yml'], :encoding => 'SHIFT_JIS') {|t|
+ t.write '--- こんにちは!'
+ t.close
+
+ # If the external encoding isn't utf8, utf16le, or utf16be, we cannot
+ # process the file.
+ File.open(t.path, 'r', :encoding => 'SHIFT_JIS') do |f|
+ assert_raises Psych::SyntaxError do
+ Psych.load(f)
+ end
end
- end
-
- t.close(true)
+ }
end
def test_io_utf16le
- t = Tempfile.new(['utf16le', 'yml'])
- t.binmode
- t.write '--- こんにちは!'.encode('UTF-16LE')
- t.close
+ Tempfile.create(['utf16le', 'yml']) {|t|
+ t.binmode
+ t.write '--- こんにちは!'.encode('UTF-16LE')
+ t.close
- File.open(t.path, 'rb', :encoding => 'UTF-16LE') do |f|
- assert_equal "こんにちは!", Psych.load(f)
- end
-
- t.close(true)
+ File.open(t.path, 'rb', :encoding => 'UTF-16LE') do |f|
+ assert_equal "こんにちは!", Psych.load(f)
+ end
+ }
end
def test_io_utf16be
- t = Tempfile.new(['utf16be', 'yml'])
- t.binmode
- t.write '--- こんにちは!'.encode('UTF-16BE')
- t.close
-
- File.open(t.path, 'rb', :encoding => 'UTF-16BE') do |f|
- assert_equal "こんにちは!", Psych.load(f)
- end
+ Tempfile.create(['utf16be', 'yml']) {|t|
+ t.binmode
+ t.write '--- こんにちは!'.encode('UTF-16BE')
+ t.close
- t.close(true)
+ File.open(t.path, 'rb', :encoding => 'UTF-16BE') do |f|
+ assert_equal "こんにちは!", Psych.load(f)
+ end
+ }
end
def test_io_utf8
- t = Tempfile.new(['utf8', 'yml'])
- t.binmode
- t.write '--- こんにちは!'.encode('UTF-8')
- t.close
-
- File.open(t.path, 'rb', :encoding => 'UTF-8') do |f|
- assert_equal "こんにちは!", Psych.load(f)
- end
+ Tempfile.create(['utf8', 'yml']) {|t|
+ t.binmode
+ t.write '--- こんにちは!'.encode('UTF-8')
+ t.close
- t.close(true)
+ File.open(t.path, 'rb', :encoding => 'UTF-8') do |f|
+ assert_equal "こんにちは!", Psych.load(f)
+ end
+ }
end
def test_emit_alias
@@ -114,19 +115,14 @@ module Psych
end
def test_to_yaml_is_valid
- ext_before = Encoding.default_external
- int_before = Encoding.default_internal
-
- Encoding.default_external = Encoding::US_ASCII
- Encoding.default_internal = nil
-
- s = "こんにちは!"
- # If no encoding is specified, use UTF-8
- assert_equal Encoding::UTF_8, Psych.dump(s).encoding
- assert_equal s, Psych.load(Psych.dump(s))
- ensure
- Encoding.default_external = ext_before
- Encoding.default_internal = int_before
+ with_default_external(Encoding::US_ASCII) do
+ with_default_internal(nil) do
+ s = "こんにちは!"
+ # If no encoding is specified, use UTF-8
+ assert_equal Encoding::UTF_8, Psych.dump(s).encoding
+ assert_equal s, Psych.load(Psych.dump(s))
+ end
+ end
end
def test_start_mapping
@@ -191,19 +187,14 @@ module Psych
end
def test_default_internal
- before = Encoding.default_internal
-
- Encoding.default_internal = 'EUC-JP'
+ with_default_internal(Encoding::EUC_JP) do
+ str = "壁に耳あり、障子に目あり"
+ assert_equal @utf8, str.encoding
- str = "壁に耳あり、障子に目あり"
- yaml = "--- #{str}"
- assert_equal @utf8, str.encoding
-
- @parser.parse str
- assert_encodings Encoding.find('EUC-JP'), @handler.strings
- assert_equal str, @handler.strings.first.encode('UTF-8')
- ensure
- Encoding.default_internal = before
+ @parser.parse str
+ assert_encodings Encoding::EUC_JP, @handler.strings
+ assert_equal str, @handler.strings.first.encode('UTF-8')
+ end
end
def test_scalar
diff --git a/test/psych/test_engine_manager.rb b/test/psych/test_engine_manager.rb
deleted file mode 100644
index b52fd09..0000000
--- a/test/psych/test_engine_manager.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-require 'psych/helper'
-require 'yaml'
-
-module Psych
- class TestEngineManager < TestCase
- def teardown
- YAML::ENGINE.yamler = 'syck'
- end
-
- def test_bad_engine
- assert_raises(ArgumentError) do
- YAML::ENGINE.yamler = 'foooo'
- end
- end
-
- def test_set_psych
- YAML::ENGINE.yamler = 'psych'
- assert_equal Psych, YAML
- assert_equal 'psych', YAML::ENGINE.yamler
- end
-
- def test_set_syck
- YAML::ENGINE.yamler = 'syck'
- assert_equal ::Syck, YAML
- assert_equal 'syck', YAML::ENGINE.yamler
- end
-
- A = Struct.new(:name)
-
- def test_dump_types
- YAML::ENGINE.yamler = 'psych'
-
- assert_to_yaml ::Object.new
- assert_to_yaml Time.now
- assert_to_yaml Date.today
- assert_to_yaml('a' => 'b')
- assert_to_yaml A.new('foo')
- assert_to_yaml %w{a b}
- assert_to_yaml Exception.new('foo')
- assert_to_yaml "hello!"
- assert_to_yaml :fooo
- assert_to_yaml(1..10)
- assert_to_yaml(/hello!~/)
- assert_to_yaml 1
- assert_to_yaml 1.2
- assert_to_yaml Rational(1, 2)
- assert_to_yaml Complex(1, 2)
- assert_to_yaml true
- assert_to_yaml false
- assert_to_yaml nil
- end
-
- def assert_to_yaml obj
- assert obj.to_yaml, "#{obj.class} to_yaml works"
- end
- end
-end
diff --git a/test/psych/test_exception.rb b/test/psych/test_exception.rb
index c6d98d7..30dfb24 100644
--- a/test/psych/test_exception.rb
+++ b/test/psych/test_exception.rb
@@ -1,4 +1,4 @@
-require 'psych/helper'
+require_relative 'helper'
module Psych
class TestException < TestCase
@@ -16,6 +16,12 @@ module Psych
@wups = Wups.new
end
+ def test_naming_exception
+ err = String.xxx rescue $!
+ new_err = Psych.load(Psych.dump(err))
+ assert_equal err.message, new_err.message
+ end
+
def test_load_takes_file
ex = assert_raises(Psych::SyntaxError) do
Psych.load '--- `'
@@ -56,27 +62,27 @@ module Psych
end
def test_parse_file_exception
- t = Tempfile.new(['parsefile', 'yml'])
- t.binmode
- t.write '--- `'
- t.close
- ex = assert_raises(Psych::SyntaxError) do
- Psych.parse_file t.path
- end
- assert_equal t.path, ex.file
- t.close(true)
+ Tempfile.create(['parsefile', 'yml']) {|t|
+ t.binmode
+ t.write '--- `'
+ t.close
+ ex = assert_raises(Psych::SyntaxError) do
+ Psych.parse_file t.path
+ end
+ assert_equal t.path, ex.file
+ }
end
def test_load_file_exception
- t = Tempfile.new(['loadfile', 'yml'])
- t.binmode
- t.write '--- `'
- t.close
- ex = assert_raises(Psych::SyntaxError) do
- Psych.load_file t.path
- end
- assert_equal t.path, ex.file
- t.close(true)
+ Tempfile.create(['loadfile', 'yml']) {|t|
+ t.binmode
+ t.write '--- `'
+ t.close
+ ex = assert_raises(Psych::SyntaxError) do
+ Psych.load_file t.path
+ end
+ assert_equal t.path, ex.file
+ }
end
def test_psych_parse_takes_file
@@ -126,5 +132,26 @@ module Psych
assert_equal 1, w.foo
assert_nil w.bar
end
+
+ def test_psych_syntax_error
+ Tempfile.create(['parsefile', 'yml']) do |t|
+ t.binmode
+ t.write '--- `'
+ t.close
+
+ begin
+ Psych.parse_file t.path
+ rescue StandardError
+ assert true # count assertion
+ ensure
+ return unless $!
+
+ ancestors = $!.class.ancestors.inspect
+
+ flunk "Psych::SyntaxError not rescued by StandardError: #{ancestors}"
+ end
+ end
+ end
+
end
end
diff --git a/test/psych/test_hash.rb b/test/psych/test_hash.rb
index 4bd4edf..066df66 100644
--- a/test/psych/test_hash.rb
+++ b/test/psych/test_hash.rb
@@ -1,15 +1,64 @@
-require 'psych/helper'
+require_relative 'helper'
module Psych
class TestHash < TestCase
class X < Hash
end
+ class HashWithCustomInit < Hash
+ attr_reader :obj
+ def initialize(obj)
+ @obj = obj
+ end
+ end
+
+ class HashWithCustomInitNoIvar < Hash
+ def initialize(obj)
+ # *shrug*
+ end
+ end
+
def setup
super
@hash = { :a => 'b' }
end
+ def test_custom_initialized
+ a = [1,2,3,4,5]
+ t1 = HashWithCustomInit.new(a)
+ t2 = Psych.load(Psych.dump(t1))
+ assert_equal t1, t2
+ assert_cycle t1
+ end
+
+ def test_custom_initialize_no_ivar
+ t1 = HashWithCustomInitNoIvar.new(nil)
+ t2 = Psych.load(Psych.dump(t1))
+ assert_equal t1, t2
+ assert_cycle t1
+ end
+
+ def test_hash_with_ivars
+ @hash.instance_variable_set :@foo, 'bar'
+ dup = Psych.load Psych.dump @hash
+ assert_equal 'bar', dup.instance_variable_get(:@foo)
+ end
+
+ def test_hash_subclass_with_ivars
+ x = X.new
+ x[:a] = 'b'
+ x.instance_variable_set :@foo, 'bar'
+ dup = Psych.load Psych.dump x
+ assert_cycle x
+ assert_equal 'bar', dup.instance_variable_get(:@foo)
+ assert_equal X, dup.class
+ end
+
+ def test_load_with_class_syck_compatibility
+ hash = Psych.load "--- !ruby/object:Hash\n:user_id: 7\n:username: Lucas\n"
+ assert_equal({ user_id: 7, username: 'Lucas'}, hash)
+ end
+
def test_empty_subclass
assert_match "!ruby/hash:#{X}", Psych.dump(X.new)
x = Psych.load Psych.dump X.new
diff --git a/test/psych/test_json_tree.rb b/test/psych/test_json_tree.rb
index eed8cf3..a23fc1a 100644
--- a/test/psych/test_json_tree.rb
+++ b/test/psych/test_json_tree.rb
@@ -1,4 +1,4 @@
-require 'psych/helper'
+require_relative 'helper'
module Psych
class TestJSONTree < TestCase
@@ -45,7 +45,7 @@ module Psych
def test_list_to_json
list = %w{ one two }
json = Psych.to_json(list)
- assert_match(/]$/, json)
+ assert_match(/\]$/, json)
assert_match(/^\[/, json)
assert_match(/"one"/, json)
assert_match(/"two"/, json)
diff --git a/test/psych/test_marshalable.rb b/test/psych/test_marshalable.rb
new file mode 100644
index 0000000..7df74ee
--- /dev/null
+++ b/test/psych/test_marshalable.rb
@@ -0,0 +1,54 @@
+require_relative 'helper'
+require 'delegate'
+
+module Psych
+ class TestMarshalable < TestCase
+ def test_objects_defining_marshal_dump_and_marshal_load_can_be_dumped
+ sd = SimpleDelegator.new(1)
+ loaded = Psych.load(Psych.dump(sd))
+
+ assert_instance_of(SimpleDelegator, loaded)
+ assert_equal(sd, loaded)
+ end
+
+ class PsychCustomMarshalable < BasicObject
+ attr_reader :foo
+
+ def initialize(foo)
+ @foo = foo
+ end
+
+ def marshal_dump
+ [foo]
+ end
+
+ def mashal_load(data)
+ @foo = data[0]
+ end
+
+ def init_with(coder)
+ @foo = coder['foo']
+ end
+
+ def encode_with(coder)
+ coder['foo'] = 2
+ end
+
+ def respond_to?(method)
+ [:marshal_dump, :marshal_load, :init_with, :encode_with].include?(method)
+ end
+
+ def class
+ PsychCustomMarshalable
+ end
+ end
+
+ def test_init_with_takes_priority_over_marshal_methods
+ obj = PsychCustomMarshalable.new(1)
+ loaded = Psych.load(Psych.dump(obj))
+
+ assert(PsychCustomMarshalable === loaded)
+ assert_equal(2, loaded.foo)
+ end
+ end
+end
diff --git a/test/psych/test_merge_keys.rb b/test/psych/test_merge_keys.rb
index bf5968f..1620a6a 100644
--- a/test/psych/test_merge_keys.rb
+++ b/test/psych/test_merge_keys.rb
@@ -1,7 +1,106 @@
-require 'psych/helper'
+require_relative 'helper'
module Psych
class TestMergeKeys < TestCase
+ class Product
+ attr_reader :bar
+ end
+
+ def test_merge_key_with_bare_hash
+ doc = Psych.load <<-eodoc
+map:
+ <<:
+ hello: world
+ eodoc
+ hash = { "map" => { "hello" => "world" } }
+ assert_equal hash, doc
+ end
+
+ def test_roundtrip_with_chevron_key
+ h = {}
+ v = { 'a' => h, '<<' => h }
+ assert_cycle v
+ end
+
+ def test_explicit_string
+ doc = Psych.load <<-eoyml
+a: &me { hello: world }
+b: { !!str '<<': *me }
+eoyml
+ expected = {
+ "a" => { "hello" => "world" },
+ "b" => {
+ "<<" => { "hello" => "world" }
+ }
+ }
+ assert_equal expected, doc
+ end
+
+ def test_mergekey_with_object
+ s = <<-eoyml
+foo: &foo
+ bar: 10
+product:
+ !ruby/object:#{Product.name}
+ <<: *foo
+ eoyml
+ hash = Psych.load s
+ assert_equal({"bar" => 10}, hash["foo"])
+ product = hash["product"]
+ assert_equal 10, product.bar
+ end
+
+ def test_merge_nil
+ yaml = <<-eoyml
+defaults: &defaults
+development:
+ <<: *defaults
+ eoyml
+ assert_equal({'<<' => nil }, Psych.load(yaml)['development'])
+ end
+
+ def test_merge_array
+ yaml = <<-eoyml
+foo: &hello
+- 1
+baz:
+ <<: *hello
+ eoyml
+ assert_equal({'<<' => [1]}, Psych.load(yaml)['baz'])
+ end
+
+ def test_merge_is_not_partial
+ yaml = <<-eoyml
+default: &default
+ hello: world
+foo: &hello
+- 1
+baz:
+ <<: [*hello, *default]
+ eoyml
+ doc = Psych.load yaml
+ refute doc['baz'].key? 'hello'
+ assert_equal({'<<' => [[1], {"hello"=>"world"}]}, Psych.load(yaml)['baz'])
+ end
+
+ def test_merge_seq_nil
+ yaml = <<-eoyml
+foo: &hello
+baz:
+ <<: [*hello]
+ eoyml
+ assert_equal({'<<' => [nil]}, Psych.load(yaml)['baz'])
+ end
+
+ def test_bad_seq_merge
+ yaml = <<-eoyml
+defaults: &defaults [1, 2, 3]
+development:
+ <<: *defaults
+ eoyml
+ assert_equal({'<<' => [1,2,3]}, Psych.load(yaml)['development'])
+ end
+
def test_missing_merge_key
yaml = <<-eoyml
bar:
diff --git a/test/psych/test_nil.rb b/test/psych/test_nil.rb
index 4f32b77..3dbf562 100644
--- a/test/psych/test_nil.rb
+++ b/test/psych/test_nil.rb
@@ -1,4 +1,4 @@
-require 'psych/helper'
+require_relative 'helper'
module Psych
class TestNil < TestCase
diff --git a/test/psych/test_null.rb b/test/psych/test_null.rb
index 0fee1d2..1725550 100644
--- a/test/psych/test_null.rb
+++ b/test/psych/test_null.rb
@@ -1,4 +1,4 @@
-require 'psych/helper'
+require_relative 'helper'
module Psych
###
diff --git a/test/psych/test_numeric.rb b/test/psych/test_numeric.rb
index bae723a..5378b4a 100644
--- a/test/psych/test_numeric.rb
+++ b/test/psych/test_numeric.rb
@@ -1,4 +1,4 @@
-require 'psych/helper'
+require_relative 'helper'
require 'bigdecimal'
module Psych
@@ -7,6 +7,19 @@ module Psych
# http://yaml.org/type/float.html
# http://yaml.org/type/int.html
class TestNumeric < TestCase
+ def setup
+ @old_debug = $DEBUG
+ $DEBUG = true
+ end
+
+ def teardown
+ $DEBUG = @old_debug
+ end
+
+ def test_load_float_with_dot
+ assert_equal 1.0, Psych.load('--- 1.')
+ end
+
def test_non_float_with_0
str = Psych.load('--- 090')
assert_equal '090', str
@@ -21,5 +34,12 @@ module Psych
decimal = BigDecimal("12.34")
assert_cycle decimal
end
+
+ def test_does_not_attempt_numeric
+ str = Psych.load('--- 4 roses')
+ assert_equal '4 roses', str
+ str = Psych.load('--- 1.1.1')
+ assert_equal '1.1.1', str
+ end
end
end
diff --git a/test/psych/test_object.rb b/test/psych/test_object.rb
index 6145bb6..5e3ce82 100644
--- a/test/psych/test_object.rb
+++ b/test/psych/test_object.rb
@@ -1,4 +1,4 @@
-require 'psych/helper'
+require_relative 'helper'
module Psych
class Tagged
diff --git a/test/psych/test_object_references.rb b/test/psych/test_object_references.rb
index 2a659a3..273b466 100644
--- a/test/psych/test_object_references.rb
+++ b/test/psych/test_object_references.rb
@@ -1,4 +1,4 @@
-require 'psych/helper'
+require_relative 'helper'
module Psych
class TestObjectReferences < TestCase
@@ -26,6 +26,10 @@ module Psych
assert_reference_trip DateTime.now
end
+ def test_struct_has_references
+ assert_reference_trip Struct.new(:foo).new(1)
+ end
+
def assert_reference_trip obj
yml = Psych.dump([obj, obj])
assert_match(/\*-?\d+/, yml)
@@ -35,7 +39,7 @@ module Psych
def test_float_references
data = Psych.load <<-eoyml
----
+---\s
- &name 1.2
- *name
eoyml
@@ -56,7 +60,7 @@ module Psych
def test_regexp_references
data = Psych.load <<-eoyml
----
+---\s
- &name !ruby/regexp /pattern/i
- *name
eoyml
diff --git a/test/psych/test_omap.rb b/test/psych/test_omap.rb
index 53f55f5..36edc26 100644
--- a/test/psych/test_omap.rb
+++ b/test/psych/test_omap.rb
@@ -1,7 +1,14 @@
-require 'psych/helper'
+require_relative 'helper'
module Psych
class TestOmap < TestCase
+ def test_parse_as_map
+ o = Psych.load "--- !!omap\na: 1\nb: 2"
+ assert_kind_of Psych::Omap, o
+ assert_equal 1, o['a']
+ assert_equal 2, o['b']
+ end
+
def test_self_referential
map = Psych::Omap.new
map['foo'] = 'bar'
diff --git a/test/psych/test_parser.rb b/test/psych/test_parser.rb
index acbdd96..0abe0dd 100644
--- a/test/psych/test_parser.rb
+++ b/test/psych/test_parser.rb
@@ -1,6 +1,6 @@
# coding: utf-8
-require 'psych/helper'
+require_relative 'helper'
module Psych
class TestParser < TestCase
diff --git a/test/psych/test_psych.rb b/test/psych/test_psych.rb
index 9986699..8054bd6 100644
--- a/test/psych/test_psych.rb
+++ b/test/psych/test_psych.rb
@@ -1,4 +1,4 @@
-require 'psych/helper'
+require_relative 'helper'
require 'stringio'
require 'tempfile'
@@ -20,7 +20,7 @@ class TestPsych < Psych::TestCase
def test_canonical
yml = Psych.dump({:a => {'b' => 'c'}}, {:canonical => true})
- assert_match(/\? ! "b/, yml)
+ assert_match(/\? "b/, yml)
end
def test_header
@@ -64,7 +64,7 @@ class TestPsych < Psych::TestCase
def test_dump_file
hash = {'hello' => 'TGIF!'}
- Tempfile.open('fun.yml') do |io|
+ Tempfile.create('fun.yml') do |io|
assert_equal io, Psych.dump(hash, io)
io.rewind
assert_equal Psych.dump(hash), io.read
@@ -125,21 +125,21 @@ class TestPsych < Psych::TestCase
end
def test_load_file
- t = Tempfile.new(['yikes', 'yml'])
- t.binmode
- t.write('--- hello world')
- t.close
- assert_equal 'hello world', Psych.load_file(t.path)
- t.close(true)
+ Tempfile.create(['yikes', 'yml']) {|t|
+ t.binmode
+ t.write('--- hello world')
+ t.close
+ assert_equal 'hello world', Psych.load_file(t.path)
+ }
end
def test_parse_file
- t = Tempfile.new(['yikes', 'yml'])
- t.binmode
- t.write('--- hello world')
- t.close
- assert_equal 'hello world', Psych.parse_file(t.path).transform
- t.close(true)
+ Tempfile.create(['yikes', 'yml']) {|t|
+ t.binmode
+ t.write('--- hello world')
+ t.close
+ assert_equal 'hello world', Psych.parse_file(t.path).transform
+ }
end
def test_degenerate_strings
diff --git a/test/psych/test_safe_load.rb b/test/psych/test_safe_load.rb
new file mode 100644
index 0000000..dd299c0
--- /dev/null
+++ b/test/psych/test_safe_load.rb
@@ -0,0 +1,97 @@
+require 'psych/helper'
+
+module Psych
+ class TestSafeLoad < TestCase
+ class Foo; end
+
+ [1, 2.2, {}, [], "foo"].each do |obj|
+ define_method(:"test_basic_#{obj.class}") do
+ assert_safe_cycle obj
+ end
+ end
+
+ def test_no_recursion
+ x = []
+ x << x
+ assert_raises(Psych::BadAlias) do
+ Psych.safe_load Psych.dump(x)
+ end
+ end
+
+ def test_explicit_recursion
+ x = []
+ x << x
+ assert_equal(x, Psych.safe_load(Psych.dump(x), [], [], true))
+ end
+
+ def test_symbol_whitelist
+ yml = Psych.dump :foo
+ assert_raises(Psych::DisallowedClass) do
+ Psych.safe_load yml
+ end
+ assert_equal(:foo, Psych.safe_load(yml, [Symbol], [:foo]))
+ end
+
+ def test_symbol
+ assert_raises(Psych::DisallowedClass) do
+ assert_safe_cycle :foo
+ end
+ assert_raises(Psych::DisallowedClass) do
+ Psych.safe_load '--- !ruby/symbol foo', []
+ end
+ assert_safe_cycle :foo, [Symbol]
+ assert_safe_cycle :foo, %w{ Symbol }
+ assert_equal :foo, Psych.safe_load('--- !ruby/symbol foo', [Symbol])
+ end
+
+ def test_foo
+ assert_raises(Psych::DisallowedClass) do
+ Psych.safe_load '--- !ruby/object:Foo {}', [Foo]
+ end
+ assert_raises(Psych::DisallowedClass) do
+ assert_safe_cycle Foo.new
+ end
+ assert_kind_of(Foo, Psych.safe_load(Psych.dump(Foo.new), [Foo]))
+ end
+
+ X = Struct.new(:x)
+ def test_struct_depends_on_sym
+ assert_safe_cycle(X.new, [X, Symbol])
+ assert_raises(Psych::DisallowedClass) do
+ cycle X.new, [X]
+ end
+ end
+
+ def test_anon_struct
+ assert Psych.safe_load(<<-eoyml, [Struct, Symbol])
+--- !ruby/struct
+ foo: bar
+ eoyml
+
+ assert_raises(Psych::DisallowedClass) do
+ Psych.safe_load(<<-eoyml, [Struct])
+--- !ruby/struct
+ foo: bar
+ eoyml
+ end
+
+ assert_raises(Psych::DisallowedClass) do
+ Psych.safe_load(<<-eoyml, [Symbol])
+--- !ruby/struct
+ foo: bar
+ eoyml
+ end
+ end
+
+ private
+
+ def cycle object, whitelist = []
+ Psych.safe_load(Psych.dump(object), whitelist)
+ end
+
+ def assert_safe_cycle object, whitelist = []
+ other = cycle object, whitelist
+ assert_equal object, other
+ end
+ end
+end
diff --git a/test/psych/test_scalar.rb b/test/psych/test_scalar.rb
index 3cf6b09..e6b7697 100644
--- a/test/psych/test_scalar.rb
+++ b/test/psych/test_scalar.rb
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-require 'psych/helper'
+require_relative 'helper'
module Psych
class TestScalar < TestCase
diff --git a/test/psych/test_scalar_scanner.rb b/test/psych/test_scalar_scanner.rb
index cf0dfff..e8e423c 100644
--- a/test/psych/test_scalar_scanner.rb
+++ b/test/psych/test_scalar_scanner.rb
@@ -1,4 +1,4 @@
-require 'psych/helper'
+require_relative 'helper'
require 'date'
module Psych
@@ -7,7 +7,7 @@ module Psych
def setup
super
- @ss = Psych::ScalarScanner.new
+ @ss = Psych::ScalarScanner.new ClassLoader.new
end
def test_scan_time
@@ -21,6 +21,17 @@ module Psych
end
end
+ def test_scan_bad_time
+ [ '2001-12-15T02:59:73.1Z',
+ '2001-12-14t90:59:43.10-05:00',
+ '2001-92-14 21:59:43.10 -5',
+ '2001-12-15 92:59:43.10',
+ '2011-02-24 81:17:06 -0800',
+ ].each do |time_str|
+ assert_equal time_str, @ss.tokenize(time_str)
+ end
+ end
+
def test_scan_bad_dates
x = '2000-15-01'
assert_equal x, @ss.tokenize(x)
@@ -87,5 +98,9 @@ module Psych
def test_scan_true
assert_equal true, ss.tokenize('true')
end
+
+ def test_scan_strings_starting_with_underscores
+ assert_equal "_100", ss.tokenize('_100')
+ end
end
end
diff --git a/test/psych/test_serialize_subclasses.rb b/test/psych/test_serialize_subclasses.rb
index c822192..f597b7a 100644
--- a/test/psych/test_serialize_subclasses.rb
+++ b/test/psych/test_serialize_subclasses.rb
@@ -1,4 +1,4 @@
-require 'psych/helper'
+require_relative 'helper'
module Psych
class TestSerializeSubclasses < TestCase
diff --git a/test/psych/test_set.rb b/test/psych/test_set.rb
index bea67d9..921fe22 100644
--- a/test/psych/test_set.rb
+++ b/test/psych/test_set.rb
@@ -1,4 +1,4 @@
-require 'psych/helper'
+require_relative 'helper'
module Psych
class TestSet < TestCase
diff --git a/test/psych/test_stream.rb b/test/psych/test_stream.rb
index beca365..7e41178 100644
--- a/test/psych/test_stream.rb
+++ b/test/psych/test_stream.rb
@@ -1,4 +1,4 @@
-require 'psych/helper'
+require_relative 'helper'
module Psych
class TestStream < TestCase
diff --git a/test/psych/test_string.rb b/test/psych/test_string.rb
index c7d5c60..26a4e20 100644
--- a/test/psych/test_string.rb
+++ b/test/psych/test_string.rb
@@ -1,4 +1,5 @@
-require 'psych/helper'
+# encoding: UTF-8
+require_relative 'helper'
module Psych
class TestString < TestCase
@@ -9,6 +10,72 @@ module Psych
attr_accessor :val
end
+ class Z < String
+ def initialize
+ force_encoding Encoding::US_ASCII
+ end
+ end
+
+ def test_string_with_newline
+ assert_equal "1\n2", Psych.load("--- ! '1\n\n 2'\n")
+ end
+
+ def test_no_doublequotes_with_special_characters
+ assert_equal 2, Psych.dump(%Q{<%= ENV["PATH"] %>}).count('"')
+ end
+
+ def test_no_quotes_when_start_with_non_ascii_character
+ yaml = Psych.dump 'Český non-ASCII'.encode(Encoding::UTF_8)
+ assert_match(/---\s*[^"'!]+$/, yaml)
+ end
+
+ def test_doublequotes_when_there_is_a_single
+ yaml = Psych.dump "@123'abc"
+ assert_match(/---\s*"/, yaml)
+ end
+
+ def test_cycle_x
+ str = X.new 'abc'
+ assert_cycle str
+ end
+
+ def test_dash_dot
+ assert_cycle '-.'
+ assert_cycle '+.'
+ end
+
+ def test_string_subclass_with_anchor
+ y = Psych.load <<-eoyml
+---
+body:
+ string: &70121654388580 !ruby/string
+ str: ! 'foo'
+ x:
+ body: *70121654388580
+ eoyml
+ assert_equal({"body"=>{"string"=>"foo", "x"=>{"body"=>"foo"}}}, y)
+ end
+
+ def test_self_referential_string
+ y = Psych.load <<-eoyml
+---
+string: &70121654388580 !ruby/string
+ str: ! 'foo'
+ body: *70121654388580
+ eoyml
+
+ assert_equal({"string"=>"foo"}, y)
+ value = y['string']
+ assert_equal value, value.instance_variable_get(:@body)
+ end
+
+ def test_another_subclass_with_attributes
+ y = Psych.load Psych.dump Y.new("foo").tap {|y| y.val = 1}
+ assert_equal "foo", y
+ assert_equal Y, y.class
+ assert_equal 1, y.val
+ end
+
def test_backwards_with_syck
x = Psych.load "--- !str:#{X.name} foo\n\n"
assert_equal X, x.class
@@ -21,6 +88,12 @@ module Psych
assert_equal X, x.class
end
+ def test_empty_character_subclass
+ assert_match "!ruby/string:#{Z}", Psych.dump(Z.new)
+ x = Psych.load Psych.dump Z.new
+ assert_equal Z, x.class
+ end
+
def test_subclass_with_attributes
y = Psych.load Psych.dump Y.new.tap {|y| y.val = 1}
assert_equal Y, y.class
@@ -33,8 +106,8 @@ module Psych
assert_equal '01:03:05', Psych.load(yaml)
end
- def test_tagged_binary_should_be_dumped_as_binary
- string = "hello world!"
+ def test_nonascii_string_as_binary
+ string = "hello \x80 world!"
string.force_encoding 'ascii-8bit'
yml = Psych.dump string
assert_match(/binary/, yml)
@@ -62,12 +135,19 @@ module Psych
assert_equal string, Psych.load(yml)
end
+ def test_ascii_only_8bit_string
+ string = "abc".encode(Encoding::ASCII_8BIT)
+ yml = Psych.dump string
+ refute_match(/binary/, yml)
+ assert_equal string, Psych.load(yml)
+ end
+
def test_string_with_ivars
food = "is delicious"
ivar = "on rock and roll"
food.instance_variable_set(:@we_built_this_city, ivar)
- str = Psych.load Psych.dump food
+ Psych.load Psych.dump food
assert_equal ivar, food.instance_variable_get(:@we_built_this_city)
end
@@ -76,6 +156,10 @@ module Psych
assert_cycle string
end
+ def test_float_confusion
+ assert_cycle '1.'
+ end
+
def binary_string percentage = 0.31, length = 100
string = ''
(percentage * length).to_i.times do |i|
diff --git a/test/psych/test_struct.rb b/test/psych/test_struct.rb
index 977aae0..8c7f251 100644
--- a/test/psych/test_struct.rb
+++ b/test/psych/test_struct.rb
@@ -1,4 +1,4 @@
-require 'psych/helper'
+require_relative 'helper'
class PsychStructWithIvar < Struct.new(:foo)
attr_reader :bar
diff --git a/test/psych/test_symbol.rb b/test/psych/test_symbol.rb
index 3226141..558a672 100644
--- a/test/psych/test_symbol.rb
+++ b/test/psych/test_symbol.rb
@@ -1,7 +1,15 @@
-require 'psych/helper'
+require_relative 'helper'
module Psych
class TestSymbol < TestCase
+ def test_cycle_empty
+ assert_cycle :''
+ end
+
+ def test_cycle_colon
+ assert_cycle :':'
+ end
+
def test_cycle
assert_cycle :a
end
diff --git a/test/psych/test_tainted.rb b/test/psych/test_tainted.rb
index fdcced4..37fc5b2 100644
--- a/test/psych/test_tainted.rb
+++ b/test/psych/test_tainted.rb
@@ -1,4 +1,4 @@
-require 'psych/helper'
+require_relative 'helper'
module Psych
class TestStringTainted < TestCase
@@ -117,14 +117,14 @@ module Psych
class TestIOTainted < TestStringTainted
def assert_taintedness string
- t = Tempfile.new(['something', 'yml'])
- t.binmode
- t.write string
- t.close
- File.open(t.path, 'r:bom|utf-8') { |f|
- @parser.parse f
+ Tempfile.create(['something', 'yml']) {|t|
+ t.binmode
+ t.write string
+ t.close
+ File.open(t.path, 'r:bom|utf-8') { |f|
+ @parser.parse f
+ }
}
- t.close(true)
end
end
end
diff --git a/test/psych/test_to_yaml_properties.rb b/test/psych/test_to_yaml_properties.rb
index 2636bec..5b4860c 100644
--- a/test/psych/test_to_yaml_properties.rb
+++ b/test/psych/test_to_yaml_properties.rb
@@ -1,4 +1,4 @@
-require 'psych/helper'
+require_relative 'helper'
module Psych
class TestToYamlProperties < MiniTest::Unit::TestCase
diff --git a/test/psych/test_tree_builder.rb b/test/psych/test_tree_builder.rb
index 9a134d5..7ad3ddd 100644
--- a/test/psych/test_tree_builder.rb
+++ b/test/psych/test_tree_builder.rb
@@ -1,4 +1,4 @@
-require 'psych/helper'
+require_relative 'helper'
module Psych
class TestTreeBuilder < TestCase
diff --git a/test/psych/test_yaml.rb b/test/psych/test_yaml.rb
index 807c058..cd3e8ee 100644
--- a/test/psych/test_yaml.rb
+++ b/test/psych/test_yaml.rb
@@ -1,8 +1,8 @@
-# -*- mode: ruby; ruby-indent-level: 4; tab-width: 4 -*-
+# -*- coding: us-ascii; mode: ruby; ruby-indent-level: 4; tab-width: 4 -*-
# vim:sw=4:ts=4
# $Id$
#
-require 'psych/helper'
+require_relative 'helper'
require 'ostruct'
# [ruby-core:01946]
@@ -250,7 +250,6 @@ EOY
def test_spec_mapping_between_sequences
# Complex key #1
- dj = Date.new( 2001, 7, 23 )
assert_parse_only(
{ [ 'Detroit Tigers', 'Chicago Cubs' ] => [ Date.new( 2001, 7, 23 ) ],
[ 'New York Yankees', 'Atlanta Braves' ] => [ Date.new( 2001, 7, 2 ), Date.new( 2001, 8, 12 ), Date.new( 2001, 8, 14 ) ] }, <<EOY
@@ -606,7 +605,7 @@ EOY
def test_spec_domain_prefix
customer_proc = proc { |type, val|
if Hash === val
- scheme, domain, type = type.split( ':', 3 )
+ _, _, type = type.split( ':', 3 )
val['type'] = "domain #{type}"
val
else
@@ -1266,4 +1265,24 @@ EOY
Psych.load("2000-01-01 00:00:00.#{"0"*1000} +00:00\n")
# '[ruby-core:13735]'
end
+
+ def test_multiline_string_uses_literal_style
+ yaml = Psych.dump("multi\nline\nstring")
+ assert_match("|", yaml)
+ end
+
+ def test_string_starting_with_non_word_character_uses_double_quotes_without_exclamation_mark
+ yaml = Psych.dump("@123'abc")
+ refute_match("!", yaml)
+ end
+
+ def test_string_dump_with_colon
+ yaml = Psych.dump 'x: foo'
+ refute_match '!', yaml
+ end
+
+ def test_string_dump_starting_with_star
+ yaml = Psych.dump '*foo'
+ refute_match '!', yaml
+ end
end
diff --git a/test/psych/test_yamldbm.rb b/test/psych/test_yamldbm.rb
index d978003..7853658 100644
--- a/test/psych/test_yamldbm.rb
+++ b/test/psych/test_yamldbm.rb
@@ -1,6 +1,4 @@
-# -*- coding: UTF-8 -*-
-
-require 'psych/helper'
+require_relative 'helper'
require 'tmpdir'
begin
@@ -13,7 +11,6 @@ module Psych
class YAMLDBMTest < TestCase
def setup
- @engine, YAML::ENGINE.yamler = YAML::ENGINE.yamler, 'psych'
@dir = Dir.mktmpdir("rubytest-file")
File.chown(-1, Process.gid, @dir)
@yamldbm_file = make_tmp_filename("yamldbm")
@@ -21,7 +18,6 @@ module Psych
end
def teardown
- YAML::ENGINE.yamler = @engine
@yamldbm.clear
@yamldbm.close
FileUtils.remove_entry_secure @dir
diff --git a/test/psych/test_yamlstore.rb b/test/psych/test_yamlstore.rb
index 5d6fcb7..94f1330 100644
--- a/test/psych/test_yamlstore.rb
+++ b/test/psych/test_yamlstore.rb
@@ -1,4 +1,4 @@
-require 'psych/helper'
+require_relative 'helper'
require 'yaml/store'
require 'tmpdir'
@@ -7,7 +7,6 @@ module Psych
class YAMLStoreTest < TestCase
def setup
- @engine, YAML::ENGINE.yamler = YAML::ENGINE.yamler, 'psych'
@dir = Dir.mktmpdir("rubytest-file")
File.chown(-1, Process.gid, @dir)
@yamlstore_file = make_tmp_filename("yamlstore")
@@ -15,7 +14,6 @@ module Psych
end
def teardown
- YAML::ENGINE.yamler = @engine
FileUtils.remove_entry_secure @dir
end
diff --git a/test/psych/visitors/test_to_ruby.rb b/test/psych/visitors/test_to_ruby.rb
index 5b0702c..c13d980 100644
--- a/test/psych/visitors/test_to_ruby.rb
+++ b/test/psych/visitors/test_to_ruby.rb
@@ -1,3 +1,4 @@
+# coding: US-ASCII
require 'psych/helper'
module Psych
@@ -5,7 +6,7 @@ module Psych
class TestToRuby < TestCase
def setup
super
- @visitor = ToRuby.new
+ @visitor = ToRuby.create
end
def test_object
@@ -17,8 +18,8 @@ module Psych
assert_equal 'bar', o.instance_variable_get(:@foo)
end
- def test_awesome
- Psych.load('1900-01-01T00:00:00+00:00')
+ def test_tz_00_00_loads_without_error
+ assert Psych.load('1900-01-01T00:00:00+00:00')
end
def test_legacy_struct
@@ -87,7 +88,7 @@ description:
end
def test_exception
- exc = Exception.new 'hello'
+ exc = ::Exception.new 'hello'
mapping = Nodes::Mapping.new nil, '!ruby/exception'
mapping.children << Nodes::Scalar.new('message')
diff --git a/test/psych/visitors/test_yaml_tree.rb b/test/psych/visitors/test_yaml_tree.rb
index df77563..40702bc 100644
--- a/test/psych/visitors/test_yaml_tree.rb
+++ b/test/psych/visitors/test_yaml_tree.rb
@@ -5,7 +5,25 @@ module Psych
class TestYAMLTree < TestCase
def setup
super
- @v = Visitors::YAMLTree.new
+ @v = Visitors::YAMLTree.create
+ end
+
+ def test_tree_can_be_called_twice
+ @v.start
+ @v << Object.new
+ t = @v.tree
+ assert_equal t, @v.tree
+ end
+
+ def test_yaml_tree_can_take_an_emitter
+ io = StringIO.new
+ e = Psych::Emitter.new io
+ v = Visitors::YAMLTree.create({}, e)
+ v.start
+ v << "hello world"
+ v.finish
+
+ assert_match "hello world", io.string
end
def test_binary_formatting