pax_global_header00006660000000000000000000000064141254317610014516gustar00rootroot0000000000000052 comment=521f7261bba9f3a666522eed7d9768fcb19a682f ruby-magic-0.5.0/000077500000000000000000000000001412543176100135575ustar00rootroot00000000000000ruby-magic-0.5.0/.editorconfig000066400000000000000000000004301412543176100162310ustar00rootroot00000000000000root = true [*] charset = utf-8 end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true [{*.rb,*.rake,Rakefile}] indent_style = space indent_size = 2 [{*.yml,*.yaml}] indent_style = space indent_size = 2 [Vagrantfile] indent_size = 2 indent_style = space ruby-magic-0.5.0/.gitattributes000066400000000000000000000003271412543176100164540ustar00rootroot00000000000000* text=auto *.c eol=lf linguist-language=c *.h eol=lf linguist-language=c *.rb eol=lf diff=ruby *.gemspec eol=lf diff=ruby *.sh eol=lf Guardfile eol=lf diff=ruby Rakefile eol=lf diff=ruby .github/ export-ignore ruby-magic-0.5.0/.gitignore000066400000000000000000000007051412543176100155510ustar00rootroot00000000000000._* *~ *.[568avq] [568avq].out core *.o *.a *.obj *.elf *.dylib *.[as]o *.[568ao] *.bundle *.rbc *.exe *.exe~ *.com *.dll *.sw[a-z] .*.sw[a-z] *.[7gx]z *.[jrt]ar *.zip *.gem *.rej *.orig *.log *.test *.out .git .bundle Gemfile.lock .gems .rbenv .rbenv-gemsets .ruby-version .ruby-gemset .DS_Store .DS_Store? .AppleDouble .LSOverride .Spotlight-* .Trash* .vagrant .direnv .envrc Icon? Icon[ ] .idea .vscode pkg ports tmp ext/magic/share/ ruby-magic-0.5.0/.gitlab-ci.yml000066400000000000000000000013141412543176100162120ustar00rootroot00000000000000.test-template: &test cache: paths: - vendor/ruby before_script: - gem install bundler --no-document - bundle config set --local path 'vendor/bundle' - bundle config set --local without 'development' - bundle install -j $(nproc) --path vendor # Install dependencies into ./vendor/ruby - ruby -v # Print out ruby version for debugging script: - bundle exec rake compile - bundle exec rake test rspec-2.5: image: "ruby:2.5" <<: *test rspec-2.6: image: "ruby:2.6" <<: *test rspec-2.7: image: "ruby:2.7" <<: *test rspec-3.0: image: "ruby:3.0" <<: *test truffleruby: image: "flavorjones/truffleruby:buster" <<: *test ruby-magic-0.5.0/.mailmap000066400000000000000000000003561412543176100152040ustar00rootroot00000000000000Krzysztof Wilczyński kwilczynski Krzysztof Wilczyński kwilczynski Krzysztof Wilczyński Krzysztof Wilczyński ruby-magic-0.5.0/.rubocop.yml000066400000000000000000000001601412543176100160260ustar00rootroot00000000000000AllCops: NewCops: enable Include: - '**/*.rb' SuggestExtensions: false Layout/LineLength: Max: 120 ruby-magic-0.5.0/.travis.yml000066400000000000000000000016441412543176100156750ustar00rootroot00000000000000dist: bionic cache: bundler: true language: ruby rvm: - 2.5 - 2.6 - 2.7 - 3.0 - truffleruby-head env: matrix: - COMPILER=gcc RAKE_COMPILER_OPTIONS="-- --enable-static" - COMPILER=clang RAKE_COMPILER_OPTIONS="-- --enable-static" - COMPILER=gcc RAKE_COMPILER_OPTIONS="-- --disable-static" - COMPILER=clang RAKE_COMPILER_OPTIONS="-- --disable-static" - COMPILER=gcc RAKE_COMPILER_OPTIONS="-- --use-system-libraries" - COMPILER=clang RAKE_COMPILER_OPTIONS="-- --use-system-libraries" branches: only: - main notifications: email: on_success: never on_failure: always before_install: - export CC="$(which $COMPILER)" - if [[ $RAKE_COMPILER_OPTIONS =~ use-system-libraries ]]; then sudo apt update && sudo apt install libmagic1 libmagic-dev; fi before_script: - bundle exec rake clean script: - bundle exec rake compile $RAKE_COMPILER_OPTIONS - bundle exec rake test ruby-magic-0.5.0/AUTHORS000066400000000000000000000000641412543176100146270ustar00rootroot00000000000000Krzysztof Wilczyński (@kwilczynski) ruby-magic-0.5.0/CHANGELOG.md000066400000000000000000000067141412543176100154000ustar00rootroot00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). ## [Unreleased] ## [0.2.0] - 2015-03-25 ### Added - Added _Gemnasium_, plus _Coveralls_ and _Code Climate_ integration (and hence improved code test coverage). - Added functionality to handle releasing the `GVL` for any _file_ and/or _I/O_ operations. - Added ability to `Magic#flags_array` to return name of each flag that is set. - Added naïve synchronization via `Mutex#lock` and `Mutex#unlock` methods to make interactions with _libmagic_ more thread-safe. - Added ability for _Travis CI_ test against multiple versions of vanilla _libmagic_. - Added _LLVM_ (`clang`) compiler to build with to _Travis CI_, and fixed various issues reported by `clang` compiler during build-time. - Added ability for `Magic#load` to take nil as valid argument. - Added support for the `MAGIC_CONTINUE` flag so that `Magic#file`, `Magic#buffer` and `Magic#descriptor` methods will return an _array_ for multiple results when appropriate. - Added rudimentary `Vagrantfile` that can be used to build a development environment. - Added the `Guard` Ruby gem for convenience, with an appropriate `Guardfile`. ### Changed - Re-factored the _API_ and cleaned up small portions of code and documentation. - Changed `Magic#version_array` and `Magic#version_string` methods to be a singleton methods. - Renamed the `Magic#flags_array` method to `Magic#flags_to_a`. - Changed the `Magic::new` method, so that it accepts an array of paths from which to load the _Magic_ database. - Moved integration with _File_ and _String_ core classes into separate namespace. - Changed the behavior not to catch the generic `Magic::Error`, plus always to raise on errors coming from _libmagic_. This is to make it more aligned with the standard library, where _file_ and _I/O_ related errors would raise an appropriate exception. - Changed behavior consistent among various versions of _libmagic_ adhering to the _POSIX_ standard. This concerns the following _IEEE 1003.1_ standards: - http://pubs.opengroup.org/onlinepubs/007904975/utilities/file.html - http://pubs.opengroup.org/onlinepubs/9699919799/utilities/file.html ### Fixed - Fixed formatting and white spaces. - Fixed _Travis CI_ build against _Rubinius_. - Fixed setting of global `errno` value to avoid race conditions. - Fixed issue with _libmagic_'s regular expression (`regex`) library not working with _UTF-8_ (or any other wide-character encoding). - Fixed build to make it work with _C++_ compilers. - Fixed any _C90_ standard related build-time warnings. - Fixed version number to comply with _Semantic Versioning 2_ (http://semver.org/). ### Deprecated - Retired support for Ruby 1.8.x (no support for _MRI_, _Ruby Enterprise Edition_ and _Rubinius_). - Retired testing with Ruby _1.9.2_ and _2.1.0_, and added _2.2.0_ on _Travis CI_. - Removed forward declaration of `errno` as it's not needed on systems with modern _C/C++_ libraries and compilers. - Removed the `-Wl,--no-undefined` option from `LDFLAGS`, as they might break on some systems. ## [0.1.0] - 2014-03-21 ### Added - First version of Magic. [Unreleased]: https://github.com/kwilczynski/ruby-magic/compare/v0.2.0...HEAD [0.2.0]: https://github.com/kwilczynski/ruby-magic/compare/v0.1.0...v0.2.0 [0.1.0]: https://github.com/kwilczynski/ruby-magic/compare/29e6c26...v0.1.0 ruby-magic-0.5.0/CODE_OF_CONDUCT.md000066400000000000000000000062241412543176100163620ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at kw@linux.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ ruby-magic-0.5.0/CONTRIBUTING.md000066400000000000000000000000311412543176100160020ustar00rootroot00000000000000See [README](README.md). ruby-magic-0.5.0/CONTRIBUTORS.md000066400000000000000000000013431412543176100160370ustar00rootroot00000000000000# Ruby Magic contributors We thank all of our [contributors][1]! The following is a list of people who have contributed at some point to the code of Ruby Magic. They are hereby acknowledged and collectively identified as "The Ruby Magic Authors". Copyright is held by the original contributor according to the versioning history; see [LICENSE](LICENSE) file. - Igor Victor ([@gogainda](https://github.com/gogainda)) - John Hawthorn ([@jhawthorn](https://github.com/jhawthorn)) - Mike Dalessio ([@flavorjones](https://github.com/flavorjones)) - Shinya Maeda ([@dosuken123](https://github.com/dosuken123)) - Stan Hu ([@stanhu](https://github.com/stanhu)) [1]: https://github.com/kwilczynski/ruby-magic/graphs/contributors "Contributors" ruby-magic-0.5.0/COPYRIGHT000066400000000000000000000000521412543176100150470ustar00rootroot00000000000000Copyright 2013-2021 Krzysztof Wilczyński ruby-magic-0.5.0/Gemfile000066400000000000000000000004201412543176100150460ustar00rootroot00000000000000source 'https://rubygems.org' gemspec gem 'rake' gem 'rake-compiler' gem 'rake-compiler-dock' gem 'rdoc' group :test do gem 'mocha' gem 'rubocop', require: false gem 'test-unit' end group :development do gem 'guard' gem 'guard-bundler' gem 'guard-rake' end ruby-magic-0.5.0/Guardfile000066400000000000000000000004061412543176100154040ustar00rootroot00000000000000guard 'bundler' do watch('Gemfile') end guard 'rake', task: 'compile' do watch(%r{^ext/(.+)\.(c|h)}) watch(%r{^lib/(.+)\.rb}) end guard 'rake', task: 'test' do watch(%r{^ext/(.+)\.(c|h)}) watch(%r{^lib/(.+)\.rb}) watch(%r{^test/test_(.+)\.rb}) end ruby-magic-0.5.0/LICENSE000066400000000000000000000261361412543176100145740ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ruby-magic-0.5.0/MAINTAINERS000066400000000000000000000002201412543176100152460ustar00rootroot00000000000000Krzysztof Wilczyński (@kwilczynski) Mike Dalessio (@flavorjones) Stan Hu (@stanhu) ruby-magic-0.5.0/NOTICE000066400000000000000000000461771412543176100145020ustar00rootroot00000000000000Ruby Magic Copyright 2013-2021 Krzysztof Wilczyński This product relies on and/or includes a number of Open Source software projects, including those that have been modified. * ruby The Ruby Programming Language. LICENSE: The Ruby License HOMEPAGE: https://www.ruby-lang.org Ruby is copyrighted free software by Yukihiro Matsumoto . You can redistribute it and/or modify it under either the terms of the 2-clause BSDL (see the file BSDL), or the conditions below: 1. You may make and give away verbatim copies of the source form of the software without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may modify your copy of the software in any way, provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or by allowing the author to include your modifications in the software. b) use the modified software only within your corporation or organization. c) give non-standard binaries non-standard names, with instructions on where to get the original software distribution. d) make other distribution arrangements with the author. 3. You may distribute the software in object code or binary form, provided that you do at least ONE of the following: a) distribute the binaries and library files of the software, together with instructions (in the manual page or equivalent) on where to get the original distribution. b) accompany the distribution with the machine-readable source of the software. c) give non-standard binaries non-standard names, with instructions on where to get the original software distribution. d) make other distribution arrangements with the author. 4. You may modify and include the part of the software into any other software (possibly commercial). But some files in the distribution are not written by the author, so that they are not under these terms. For the list of those files and their copying conditions, see the file LEGAL. 5. The scripts and library files supplied as input to or produced as output from the software do not automatically fall under the copyright of the software, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this software. 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * rake A make-like build utility for Ruby. LICENSE: MIT HOMEPAGE: https://ruby.github.io/rake Copyright (c) Jim Weirich 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. * rdoc RDoc - Ruby Documentation System. LICENSE: The Ruby License HOMEPAGE: https://ruby.github.io/rdoc RDoc is copyrighted free software. You can redistribute it and/or modify it under either the terms of the GPL version 2 (see the file GPL), or the conditions below: 1. You may make and give away verbatim copies of the source form of the software without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may modify your copy of the software in any way, provided that you do at least ONE of the following: a. place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or by allowing the author to include your modifications in the software. b. use the modified software only within your corporation or organization. c. give non-standard binaries non-standard names, with instructions on where to get the original software distribution. d. make other distribution arrangements with the author. 3. You may distribute the software in object code or binary form, provided that you do at least ONE of the following: a. distribute the binaries and library files of the software, together with instructions (in the manual page or equivalent) on where to get the original distribution. b. accompany the distribution with the machine-readable source of the software. c. give non-standard binaries non-standard names, with instructions on where to get the original software distribution. d. make other distribution arrangements with the author. 4. You may modify and include the part of the software into any other software (possibly commercial). But some files in the distribution are not written by the author, so that they are not under these terms. For the list of those files and their copying conditions, see the file LEGAL. 5. The scripts and library files supplied as input to or produced as output from the software do not automatically fall under the copyright of the software, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this software. 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * rake-compiler Provide a standard and simplified way to build and package Ruby C and Java extensions using Rake as glue. LICENSE: MIT HOMEPAGE: https://github.com/rake-compiler/rake-compiler Copyright (c) 2008-2011 Luis Lavena. 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. * mocha Mocha is a mocking and stubbing library for Ruby. LICENSE: The Ruby License HOMEPAGE: https://mocha.jamesmead.org/ Copyright Revieworld Ltd. 2006 Ruby is copyrighted free software by Yukihiro Matsumoto . You can redistribute it and/or modify it under either the terms of the 2-clause BSDL (see the file BSDL), or the conditions below: 1. You may make and give away verbatim copies of the source form of the software without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may modify your copy of the software in any way, provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or by allowing the author to include your modifications in the software. b) use the modified software only within your corporation or organization. c) give non-standard binaries non-standard names, with instructions on where to get the original software distribution. d) make other distribution arrangements with the author. 3. You may distribute the software in object code or binary form, provided that you do at least ONE of the following: a) distribute the binaries and library files of the software, together with instructions (in the manual page or equivalent) on where to get the original distribution. b) accompany the distribution with the machine-readable source of the software. c) give non-standard binaries non-standard names, with instructions on where to get the original software distribution. d) make other distribution arrangements with the author. 4. You may modify and include the part of the software into any other software (possibly commercial). But some files in the distribution are not written by the author, so that they are not under these terms. For the list of those files and their copying conditions, see the file LEGAL. 5. The scripts and library files supplied as input to or produced as output from the software do not automatically fall under the copyright of the software, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this software. 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * test-unit An xUnit family unit testing framework for Ruby. LICENSE: The Ruby License HOMEPAGE: https://test-unit.github.io test-unit is copyrighted free software by Kouhei Sutou , Ryan Davis and Nathaniel Talbott . You can redistribute it and/or modify it under either the terms of the GPL version 2 (see the file GPL), or the conditions below: 1. You may make and give away verbatim copies of the source form of the software without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may modify your copy of the software in any way, provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or by allowing the author to include your modifications in the software. b) use the modified software only within your corporation or organization. c) give non-standard binaries non-standard names, with instructions on where to get the original software distribution. d) make other distribution arrangements with the author. 3. You may distribute the software in object code or binary form, provided that you do at least ONE of the following: a) distribute the binaries and library files of the software, together with instructions (in the manual page or equivalent) on where to get the original distribution. b) accompany the distribution with the machine-readable source of the software. c) give non-standard binaries non-standard names, with instructions on where to get the original software distribution. d) make other distribution arrangements with the author. 4. You may modify and include the part of the software into any other software (possibly commercial). But some files in the distribution are not written by the author, so that they are not under these terms. For the list of those files and their copying conditions, see the file LEGAL. 5. The scripts and library files supplied as input to or produced as output from the software do not automatically fall under the copyright of the software, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this software. 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * guard Guard is a command line tool to easily handle events on file system modifications. LICENSE: MIT HOMEPAGE: https://guardgem.org/ Copyright (c) 2009-2016 Thibaud Guillaume-Gentil 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. * guard-bundler Guard::Bundler automatically install/update your gem bundle when needed. LICENSE: MIT HOMEPAGE: https://github.com/guard/guard-bundler Copyright (c) 2010-2014 Yann Lugrin, Rémy Coutable 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. * guard-rake guard-rake runs a rake task when files change. LICENSE: MIT HOMEPAGE: https://github.com/rubyist/guard-rake Copyright (c) 2011 Scott Barron 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. * rubocop LICENSE: MIT HOMEPAGE: https://github.com/rubocop-hq/rubocop Copyright (c) 2012-20 Bozhidar Batsov 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. * file file(1) Command and the libmagic(3) library. LICENSE: BSD, CDDL HOMEPAGE: http://darwinsys.com/file Copyright (c) Ian F. Darwin 1986, 1987, 1989, 1990, 1991, 1992, 1994, 1995. Software written by Ian F. Darwin and others; maintained 1994- Christos Zoulas. This software is not subject to any export provision of the United States Department of Commerce, and may be exported to any country or planet. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice immediately at the beginning of the file, without modification, this list of conditions, and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ruby-magic-0.5.0/README.md000066400000000000000000000027311412543176100150410ustar00rootroot00000000000000# File Magic in Ruby [![Build Status](https://travis-ci.com/kwilczynski/ruby-magic.svg)](https://travis-ci.com/kwilczynski/ruby-magic) [![Documentation Status](https://inch-ci.org/github/kwilczynski/ruby-magic.svg)](https://inch-ci.org/github/kwilczynski/ruby-magic) [![Gem Version](https://badge.fury.io/rb/ruby-magic.svg)](http://badge.fury.io/rb/ruby-magic) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) ## Introduction Provides simple interface to [libmagic][1] for Ruby Programming Language. ## Table of Contents 1. [Contributing](#contributing) 2. [Versioning](#versioning) 3. [Author](#author) 4. [Contributors](#contributors) 4. [Copyright](#copyright) 5. [License](#license) ## Contributing See the [CONTRIBUTING.md](CONTRIBUTING.md) for best practices and instructions on setting up your development environment. ## Versioning This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). For the versions available, see the tags on this repository. ## Author Krzysztof Wilczyński () ## Contributors See the [CONTRIBUTORS.md](CONTRIBUTORS.md) file for a list of contributors to the project. ## Copyright Copyright 2013-2021 Krzysztof Wilczyński ## License This project is licensed under the terms of the Apache License, Version 2.0 license. To view the full license see the included [LICENSE](LICENSE) file. [1]: https://en.wikipedia.org/wiki/File_(command) ruby-magic-0.5.0/Rakefile000066400000000000000000000074671412543176100152420ustar00rootroot00000000000000require 'bundler/gem_tasks' require 'rake' require 'rake/clean' require 'rake/extensiontask' require 'rake/testtask' require 'rdoc/task' require 'rubocop/rake_task' RakeFileUtils.verbose_flag = false CLEAN.include FileList['**/*{.o,.so,.dylib,.bundle}'], FileList['**/extconf.h'], FileList['**/Makefile'], FileList['pkg/'], FileList['ext/magic/share/'] CLOBBER.include FileList['**/tmp'], FileList['**/*.log'], FileList['doc/**'], FileList['tmp/'] CLOBBER.add("ports/*").exclude(%r{ports/archives$}) RUBY_MAGIC_GEM_SPEC = Gem::Specification.load('ruby-magic.gemspec') RDoc::Task.new do |d| d.title = 'File Magic in Ruby' d.main = 'README.md' d.options << '--line-numbers' d.rdoc_dir = 'doc/rdoc' d.rdoc_files.include FileList['ext/**/*.{c,h}', 'lib/**/*.rb'] d.rdoc_files.include.add(%w[ AUTHORS COPYRIGHT LICENSE NOTICE CHANGELOG.md CONTRIBUTORS.md README.md ]) end Rake::TestTask.new do |t| t.test_files = FileList['test/**/test_*'] t.warning = true end RuboCop::RakeTask.new('lint') do |t| t.patterns = FileList['lib/**/*.rb', 'test/**/*.rb'] t.fail_on_error = false end Gem::PackageTask.new(RUBY_MAGIC_GEM_SPEC) do |p| p.need_zip = false p.need_tar = false end CROSS_RUBY_PLATFORMS = ["x86_64-linux", "x86-linux"] CROSS_RUBY_VERSIONS = ["2.5.0", "2.6.0", "2.7.0", "3.0.0"].join(":") require "rake_compiler_dock" ENV["RUBY_CC_VERSION"] = CROSS_RUBY_VERSIONS Rake::ExtensionTask.new('magic', RUBY_MAGIC_GEM_SPEC) do |e| e.source_pattern = '*.{c,h}' e.ext_dir = 'ext/magic' e.lib_dir = 'lib/magic' e.cross_compile = true e.cross_config_options << "--enable-cross-build" e.cross_platform = CROSS_RUBY_PLATFORMS e.cross_compiling do |spec| spec.files.reject! { |path| File.fnmatch?('ports/*', path) } spec.files.reject! { |path| File.fnmatch?('patches/*', path) } spec.dependencies.reject! { |dep| dep.name=='mini_portile2' } db_glob = "ext/magic/share/*.mgc" raise "magic files are not present at #{db_glob}" if Dir[db_glob].empty? Dir[db_glob].each { |db| spec.files << db } end end namespace "gem" do def gem_builder(platform) # use Task#invoke because the pkg/*gem task is defined at runtime Rake::Task["native:#{platform}"].invoke Rake::Task["pkg/#{RUBY_MAGIC_GEM_SPEC.full_name}-#{Gem::Platform.new(platform).to_s}.gem"].invoke end CROSS_RUBY_PLATFORMS.each do |platform| desc "build native gem for #{platform} platform" task platform do RakeCompilerDock.sh <<~EOT, platform: platform gem install bundler --no-document && bundle && bundle exec rake gem:#{platform}:builder MAKE='nice make -j`nproc`' EOT end namespace platform do desc "build native gem for #{platform} platform (guest container)" task "builder" do gem_builder(platform) end end end desc "build all native gems" task "native" => CROSS_RUBY_PLATFORMS end def add_file_to_gem(relative_source_path) dest_path = File.join(gem_build_path, relative_source_path) dest_dir = File.dirname(dest_path) mkdir_p dest_dir unless Dir.exist?(dest_dir) rm_f dest_path if File.exist?(dest_path) safe_ln relative_source_path, dest_path RUBY_MAGIC_GEM_SPEC.files << relative_source_path end def gem_build_path File.join "pkg", RUBY_MAGIC_GEM_SPEC.full_name end def add_libmagic_and_patches dependencies = YAML.load_file(File.join(File.dirname(__FILE__), "dependencies.yml")) archive = File.join("ports", "archives", "file-#{dependencies["libmagic"]["version"]}.tar.gz") add_file_to_gem archive patches = Dir['patches/**/*.patch'] patches.each { |patch| add_file_to_gem patch } end task gem_build_path do add_libmagic_and_patches end task('default').clear task default: %w[lint test] ruby-magic-0.5.0/VERSION000066400000000000000000000000061412543176100146230ustar00rootroot000000000000000.5.0 ruby-magic-0.5.0/Vagrantfile000066400000000000000000000047761412543176100157620ustar00rootroot00000000000000# A little helper to determine underlying platform. def platform_bits ['x'].pack('P').size * 8 end # Simple provisioning elements. script = %{ #!/bin/bash set -e export PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin echo 'Starting provisioning ...' # Avoid the configuration file questions ... cat <<'EOF' | sudo tee /etc/apt/apt.conf.d/99vagrant &>/dev/null DPkg::options { "--force-confdef"; "--force-confnew"; } EOF # Disable need for user interaction ... export DEBIAN_FRONTEND=noninteractive # Update package repositories ... sudo apt-get update &> /dev/null # Install needed build time dependencies ... sudo apt-get install --force-yes -y \ curl \ git \ gawk \ make \ patch \ bison \ flex \ build-essential \ sqlite3 \ libsqlite3-dev \ openssl \ pkg-config \ autoconf \ automake \ autotools-dev \ libffi-dev \ libc6-dev \ libssl-dev \ zlib1g \ zlib1g-dev \ libncurses5-dev \ libreadline6 \ libreadline6-dev \ libltdl-dev \ libyaml-dev \ libxml2-dev \ libxslt1-dev \ libtool \ libtool-doc &> /dev/null # Remove existing "libmagic-dev" package. sudo apt-get remove --purge --force-yes -y libmagic-dev &> /dev/null # Clean up unneeded packages ... { sudo apt-get autoremove --force-yes -y sudo apt-get autoclean --force-yes -y sudo apt-get clean --force-yes -y } &> /dev/null # Download and install "rvm" for local user. { cat <<'EOS' | sudo -u vagrant -- bash -s curl -s -k -L https://rvm.io/mpapis.asc | gpg --import - curl -s -k -L https://get.rvm.io | bash -s stable --ruby EOS } &> /dev/null echo 'All done!' } # Select appropriate Vagrant box per underlying architecture. box = "precise#{platform_bits}" # Virtual Machine name et al. name = "ruby-magic-#{box}" Vagrant.configure("2") do |config| config.ssh.forward_agent = true config.vm.define name.to_sym do |machine| machine.vm.box = "hashicorp/#{box}" machine.vm.box_check_update = false machine.vm.hostname = name machine.vm.provider :virtualbox do |vb| vb.name = name vb.gui = false vb.customize ['modifyvm', :id, '--memory', '384', '--cpus', '1', '--rtcuseutc', 'on', '--natdnshostresolver1', 'on', '--natdnsproxy1', 'on' ] end machine.vm.provision :shell, privileged: false, inline: script end end ruby-magic-0.5.0/checksum/000077500000000000000000000000001412543176100153615ustar00rootroot00000000000000ruby-magic-0.5.0/checksum/ruby-magic-0.2.0.gem.SHA256000066400000000000000000000001271412543176100214140ustar00rootroot00000000000000785ad89e578509480dd858c0d670915cd73b302b036b393f8579b177f30f287b ruby-magic-0.2.0.gem ruby-magic-0.5.0/checksum/ruby-magic-0.2.0.gem.SHA256.sig000066400000000000000000000010661412543176100222000ustar00rootroot000000000000003!}]|||dv=4_; |dv=4P@{Pm!ɕۤ' *tW67uMuבCKα!d>4FB(ϒl݌E*Dia uOvOma&B(-n=tWW<=_Jav%tCy [-3T})OI2g8dquF@ +=NUNU0b),N>w99BnGޣ/;&GȁLZނ{딴'd@/g;uC8k# 0 .Vi6~6_@UFLe@o7X`ѯ8գb!bkW$JfSmO̧` 0wEH+*LURte9ItUr݉E*=ٿ:8`wHB=h"']ZҤ/<8múu!ݱ$v|%N-2;B*̎5!`)_2Uruby-magic-0.5.0/dependencies.yml000066400000000000000000000017401412543176100167320ustar00rootroot00000000000000libmagic: version: "5.39" sha256: "f05d286a76d9556243d0cb05814929c2ecf3a5ba07963f8f70bfaaa70517fad1" # Manually verified checksum: # # $ gpg --verify file-5.39.tar.gz.asc file-5.39.tar.gz # gpg: Signature made Mon Jun 15 02:06:08 2020 CEST # gpg: using DSA key 0x71112AB16CB33B3A # gpg: Good signature from "Christos Zoulas (personal key) " [unknown] # gpg: aka "Christos Zoulas (NetBSD key) " [unknown] # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: BE04 995B A8F9 0ED0 C0C1 76C4 7111 2AB1 6CB3 3B3A # # Using this PGP signature: # # -----BEGIN PGP SIGNATURE----- # # iEYEABECAAYFAl7mu3AACgkQcREqsWyzOzppXACgoqokKabq7bwN9aKvd/H/yhF+ # 9PUAoOPrAtxFkfNUvtqK/trY+v/Gk5JM # =tA6a # -----END PGP SIGNATURE----- ruby-magic-0.5.0/ext/000077500000000000000000000000001412543176100143575ustar00rootroot00000000000000ruby-magic-0.5.0/ext/magic/000077500000000000000000000000001412543176100154375ustar00rootroot00000000000000ruby-magic-0.5.0/ext/magic/common.h000066400000000000000000000074021412543176100171030ustar00rootroot00000000000000#if !defined(_COMMON_H) #define _COMMON_H 1 #if !defined(_GNU_SOURCE) # define _GNU_SOURCE 1 #endif #if !defined(_BSD_SOURCE) # define _BSD_SOURCE 1 #endif #if !defined(_DEFAULT_SOURCE) # define _DEFAULT_SOURCE 1 #endif #if defined(__cplusplus) extern "C" { #endif #include #include #include #include #include #include #include #include #include #include #include #include #if defined(HAVE_RUBY_IO_H) # include #else # include #endif /* HAVE_RUBY_IO_H */ #define BIT(n) (1 << (n)) #if !defined(UNUSED) # define UNUSED(x) (void)(x) #endif /* UNUSED */ #define ARRAY_SIZE(a) (int)(sizeof(a) / sizeof((a)[0])) #if defined(F_DUPFD_CLOEXEC) # define HAVE_F_DUPFD_CLOEXEC 1 #endif /* F_DUPFD_CLOEXEC */ #if defined(O_CLOEXEC) # define HAVE_O_CLOEXEC 1 #endif /* O_CLOEXEC */ #if defined(POSIX_CLOSE_RESTART) # define HAVE_POSIX_CLOSE_RESTART 1 #endif /* POSIX_CLOSE_RESTART */ #define MAGIC_EXTENSION_SEPARATOR "/" #define MAGIC_CONTINUE_SEPARATOR "\n- " #if !defined(MAGIC_SYSTEM_LIBRARIES) # define MAGIC_STATUS_CHECK(x) x #else # define MAGIC_STATUS_CHECK(x) 1 #endif /* MAGIC_SYSTEM_LIBRARIES */ #if !defined(MAGIC_CUSTOM_CHECK_TYPE) # define MAGIC_CHECK_RUBY_TYPE(o, t) Check_Type((o), (t)) #else # define MAGIC_CHECK_RUBY_TYPE(o, t) magic_check_ruby_type((o), (t)) #endif /* MAGIC_CUSTOM_CHECK_TYPE */ #if !defined(MAGIC_NO_CHECK_CSV) # define MAGIC_NO_CHECK_CSV 0 #endif /* MAGIC_NO_CHECK_CSV */ #if !defined(MAGIC_NO_CHECK_JSON) # define MAGIC_NO_CHECK_JSON 0 #endif /* MAGIC_NO_CHECK_JSON */ #define DATA_P(x) (RB_TYPE_P((x), T_DATA)) #define BOOLEAN_P(x) (RB_TYPE_P((x), T_TRUE) || RB_TYPE_P((x), T_FALSE)) #define STRING_P(x) (RB_TYPE_P((x), T_STRING)) #define ARRAY_P(x) (RB_TYPE_P((x), T_ARRAY)) #define FILE_P(x) (RB_TYPE_P((x), T_FILE)) #define RVAL2CBOOL(b) (RTEST(b)) #define CBOOL2RVAL(b) ((b) ? Qtrue : Qfalse) #define RVAL2CSTR(s) (NIL_P(s) ? NULL : StringValueCStr(s)) #define CSTR2RVAL(s) ((s) == NULL ? Qnil : rb_str_new2((const char *)s)) #define RSTRING_EMPTY_P(s) (RSTRING_LEN(s) == 0) #define RARRAY_EMPTY rb_ary_new() #define RARRAY_EMPTY_P(a) (RARRAY_LEN(a) == 0) #define RARRAY_FIRST(a) (RARRAY_EMPTY_P(a) ? Qnil : rb_ary_entry((a), 0)) #if RUBY_API_VERSION_CODE > 20700 # define RVALUE_TYPE enum ruby_value_type #else # define RVALUE_TYPE int #endif /* RUBY_API_VERSION_CODE > 20700 */ #if RUBY_API_VERSION_CODE >= 20700 # define HAVE_RUBY_GC_COMPACT 1 # define MAGIC_GC_MARK(x) rb_gc_mark_movable((x)) #else # define MAGIC_GC_MARK(x) rb_gc_mark((x)) #endif /* RUBY_API_VERSION_CODE >= 20700 */ #define CLASS_NAME(o) (NIL_P((o)) ? "nil" : rb_obj_classname((o))) #if !defined(T_INTEGER) # define T_INTEGER rb_cInteger #endif /* T_INTEGER */ #if !defined(HAVE_RB_IO_T) # define rb_io_t OpenFile #endif /* HAVE_RB_IO_T */ #if !defined(GetReadFile) # define FPTR_TO_FD(p) ((p)->fd) #else # define FPTR_TO_FD(p) (fileno(GetReadFile(p))) #endif /* GetReadFile */ #define NOGVL_FUNCTION (VALUE(*)(void *)) #if defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL) && \ defined(HAVE_RUBY_THREAD_H) && HAVE_RUBY_THREAD_H # include # define NOGVL(f, d) \ rb_thread_call_without_gvl((f), (d), RUBY_UBF_IO, NULL) #elif defined(HAVE_RB_THREAD_BLOCKING_REGION) # define NOGVL(f, d) \ rb_thread_blocking_region(NOGVL_FUNCTION(f), (d), RUBY_UBF_IO, NULL) #else # include static inline VALUE fake_blocking_region(VALUE (*f)(ANYARGS), void *data) { VALUE rv; TRAP_BEG; rv = f(data); TRAP_END; return rv; } # define NOGVL(f, d) \ fake_blocking_region(NOGVL_FUNCTION(f), (d)) #endif /* * HAVE_RB_THREAD_CALL_WITHOUT_GVL * HAVE_RUBY_THREAD_H */ #if defined(__cplusplus) } #endif #endif /* _COMMON_H */ ruby-magic-0.5.0/ext/magic/extconf.rb000066400000000000000000000305741412543176100174430ustar00rootroot00000000000000# frozen_string_literal: true require 'find' require 'mkmf' require 'pathname' # helpful constants PACKAGE_ROOT_DIR = File.expand_path(File.join(File.dirname(__FILE__), '..', '..')) # The gem version constraint in the Rakefile is not respected at install time. # Keep this version in sync with the one in the Rakefile ! REQUIRED_MINI_PORTILE_VERSION = "~> 2.6" MAGIC_HELP_MESSAGE = <<~HELP USAGE: ruby #{$0} [options] Flags that are always valid: --use-system-libraries --enable-system-libraries Use system libraries instead of building and using the packaged libraries. --disable-system-libraries Use the packaged libraries, and ignore the system libraries. This is the default on most platforms, and overrides `--use-system-libraries` and the environment variable `MAGIC_USE_SYSTEM_LIBRARIES`. --disable-clean Do not clean out intermediate files after successful build. Flags only used when building and using the packaged libraries: --disable-static Do not statically link packaged libraries, instead use shared libraries. --enable-cross-build Enable cross-build mode. (You probably do not want to set this manually.) Flags only used when using system libraries: Related to libmagic: --with-magic-dir=DIRECTORY Look for libmagic headers and library in DIRECTORY. --with-magic-lib=DIRECTORY Look for libmagic library in DIRECTORY. --with-magic-include=DIRECTORY Look for libmagic headers in DIRECTORY. Environment variables used: CC Use this path to invoke the compiler instead of `RbConfig::CONFIG['CC']` CPPFLAGS If this string is accepted by the C preprocessor, add it to the flags passed to the C preprocessor CFLAGS If this string is accepted by the compiler, add it to the flags passed to the compiler LDFLAGS If this string is accepted by the linker, add it to the flags passed to the linker LIBS Add this string to the flags passed to the linker HELP def process_recipe(name, version, static_p, cross_p) require 'rubygems' gem('mini_portile2', REQUIRED_MINI_PORTILE_VERSION) require 'mini_portile2' message("Using mini_portile version #{MiniPortile::VERSION}\n") MiniPortile.new(name, version).tap do |recipe| # Prefer host_alias over host in order to use i586-mingw32msvc as # correct compiler prefix for cross build, but use host if not set. recipe.host = RbConfig::CONFIG["host_alias"].empty? ? RbConfig::CONFIG["host"] : RbConfig::CONFIG["host_alias"] recipe.target = File.join(PACKAGE_ROOT_DIR, "ports") recipe.patch_files = Dir[File.join(PACKAGE_ROOT_DIR, "patches", name, "*.patch")].sort recipe.configure_options << "--libdir=#{File.join(recipe.path, 'lib')}" yield recipe env = Hash.new do |hash, key| hash[key] = (ENV[key]).to_s end recipe.configure_options.flatten! recipe.configure_options.delete_if do |option| case option when /\A(\w+)=(.*)\z/ env[Regexp.last_match(1)] = if env.key?(Regexp.last_match(1)) concat_flags(env[Regexp.last_match(1)], Regexp.last_match(2)) else Regexp.last_match(2) end true else false end end recipe.configure_options = [ "--disable-silent-rules", "--disable-dependency-tracking", "--enable-fsect-man5" ] if static_p recipe.configure_options += [ "--disable-shared", "--enable-static", ] env["CFLAGS"] = concat_flags(env["CFLAGS"], "-fPIC") else recipe.configure_options += [ "--enable-shared", "--disable-static", ] end if cross_p recipe.configure_options += [ "--target=#{recipe.host}", "--host=#{recipe.host}", ] end if RbConfig::CONFIG['target_cpu'] == 'universal' %w[CFLAGS LDFLAGS].each do |key| unless env[key].include?('-arch') env[key] = concat_flags(env[key], RbConfig::CONFIG['ARCH_FLAG']) end end end recipe.configure_options += env.map do |key, value| "#{key}=#{value.strip}" end checkpoint = "#{recipe.target}/#{recipe.name}-#{recipe.version}-#{recipe.host}.installed" if File.exist?(checkpoint) message("Building Ruby Magic with a packaged version of #{name}-#{version}.\n") else message(<<~EOM) ---------- IMPORTANT NOTICE ---------- Building Ruby Magic with a packaged version of #{name}-#{version}. Configuration options: #{recipe.configure_options.shelljoin} EOM unless recipe.patch_files.empty? message("The following patches are being applied:\n") recipe.patch_files.each do |patch| message(" - %s\n" % File.basename(patch)) end end recipe.cook FileUtils.touch(checkpoint) end recipe.activate end end # # utility functions # def config_clean? enable_config('clean', true) end def config_static? default_static = !truffle? enable_config("static", default_static) end def config_cross_build? enable_config("cross-build") end def config_system_libraries? enable_config("system-libraries", ENV.key?("MAGIC_USE_SYSTEM_LIBRARIES")) do |_, default| arg_config('--use-system-libraries', default) end end def darwin? RbConfig::CONFIG['target_os'] =~ /darwin/ end def windows? RbConfig::CONFIG['target_os'] =~ /mswin|mingw32|windows/ end def truffle? ::RUBY_ENGINE == 'truffleruby' end def concat_flags(*args) args.compact.join(" ") end def do_help print(MAGIC_HELP_MESSAGE) exit!(0) end def do_clean root = Pathname(PACKAGE_ROOT_DIR) pwd = Pathname(Dir.pwd) # Skip if this is a development work tree unless (root + '.git').exist? message("Cleaning files only used during build.\n") # (root + 'tmp') cannot be removed at this stage because # libmagic.so is yet to be copied to lib. # clean the ports build directory Pathname.glob(pwd.join('tmp', '*', 'ports')) do |dir| FileUtils.rm_rf(dir, verbose: true) end FileUtils.rm_rf(root + 'ports' + 'archives', verbose: true) # Remove everything but share/ directory remove_paths = %w[bin include] remove_paths << 'lib' if config_static? Pathname.glob(File.join(root, 'ports', '*', 'libmagic', '*')) do |dir| remove_paths.each do |path| remove_dir = File.join(dir, path) FileUtils.rm_rf(remove_dir, verbose: true) end end end exit!(0) end # # main # do_help if arg_config('--help') do_clean if arg_config('--clean') if ENV['CC'] RbConfig::CONFIG['CC'] = RbConfig::MAKEFILE_CONFIG['CC'] = ENV['CC'] end ENV['CC'] = RbConfig::CONFIG['CC'] append_cflags(ENV["CFLAGS"].split) if ENV["CFLAGS"] append_cppflags(ENV["CPPFLAGS"].split) if ENV["CPPFLAGS"] append_ldflags(ENV["LDFLAGS"].split) if ENV["LDFLAGS"] $LIBS = concat_flags($LIBS, ENV["LIBS"]) if ENV["LIBS"] if truffle? $CPPFLAGS += " -DMAGIC_CUSTOM_CHECK_TYPE" end if config_system_libraries? message "Building ruby-magic using system libraries.\n" $CPPFLAGS += " -DMAGIC_SYSTEM_LIBRARIES" dir_config('magic') else message "Building ruby-magic using packaged libraries.\n" require 'yaml' dependencies = YAML.load_file(File.join(PACKAGE_ROOT_DIR, "dependencies.yml")) static_p = config_static? message "Static linking is #{static_p ? 'enabled' : 'disabled'}.\n" cross_build_p = config_cross_build? message "Cross build is #{cross_build_p ? 'enabled' : 'disabled'}.\n" libmagic_recipe = process_recipe('libmagic', dependencies["libmagic"]["version"], static_p, cross_build_p) do |recipe| recipe.files = [{ url: "https://ruby-magic.s3.nl-ams.scw.cloud/file-#{recipe.version}.tar.gz", sha256: dependencies["libmagic"]["sha256"], }] end $LIBPATH = [File.join(libmagic_recipe.path, 'lib')] $CFLAGS << " -I#{File.join(libmagic_recipe.path, 'include')} " $LDFLAGS += " -Wl,-rpath,#{libmagic_recipe.path}/lib" if static_p ENV['PKG_CONFIG_PATH'] = "#{libmagic_recipe.path}/lib/pkgconfig" # mkmf appends -- to the first option $LIBS += " " + pkg_config('libmagic', 'libs --static') $LIBS += " " + File.join(libmagic_recipe.path, 'lib', "libmagic.#{$LIBEXT}") end if cross_build_p # database files will be packaged up by the cross-compiling callback in the ExtensionTask to_path = File.join(PACKAGE_ROOT_DIR, "ext/magic/share") FileUtils.rm_rf(to_path, secure: true) FileUtils.mkdir(to_path) FileUtils.cp_r(Dir[File.join(libmagic_recipe.path, 'share/misc/*.mgc')], to_path) end end $CFLAGS += ' -std=c99' if RbConfig::CONFIG['CC'] =~ /gcc/ $CFLAGS += ' -O3' unless $CFLAGS =~ /-O\d/ end %w[ -Wcast-qual -Wwrite-strings -Wconversion -Wmissing-noreturn -Winline ].select do |flag| try_link('int main(void) { return 0; }', flag) end.each do |flag| $CFLAGS += " " + flag end unless darwin? $LDFLAGS += ' -Wl,--as-needed -Wl,--exclude-libs,ALL' end if windows? $LDFLAGS += ' -static-libgcc' end unless have_header('ruby.h') abort "\n" + (<<-EOS).gsub(/^[ ]{,3}/, '') + "\n" You appear to be missing Ruby development libraries and/or header files. You can install missing compile-time dependencies in one of the following ways: - Debian / Ubuntu apt-get install ruby-dev - Red Hat / CentOS / Fedora yum install ruby-devel or dnf install ruby-devel - Mac OS X (Darwin) brew install ruby (for Homebrew, see https://brew.sh) port install ruby2.6 (for MacPorts, see https://www.macports.org) - OpenBSD / NetBSD pkg_add ruby (for pkgsrc, see https://www.pkgsrc.org) - FreeBSD pkg install ruby (for FreeBSD Ports, see https://www.freebsd.org/ports) Alternatively, you can use either of the following Ruby version managers in order to install Ruby locally (for your user only) and/or system-wide: - Ruby Version Manager (for RVM, see https://rvm.io) - Ruby Environment (for rbenv, see https://github.com/sstephenson/rbenv) - Change Ruby (for chruby, see https://github.com/postmodern/chruby) More information about how to install Ruby on various platforms available at the following web site: https://www.ruby-lang.org/en/documentation/installation EOS end # these are needed for `rb_thread_call_without_gvl` to be properly detected on some linux systems. # specifically, rake-compiler-dock's redhat-based image needs these. have_library('pthread') have_library('rt') have_library('dl') have_library('crypt') have_func('rb_thread_call_without_gvl') have_func('rb_thread_blocking_region') unless have_header('magic.h') abort "\n" + (<<-EOS).gsub(/^[ ]{,3}/, '') + "\n" You appear to be missing libmagic(3) library and/or necessary header files. You can install missing compile-time dependencies in one of the following ways: - Debian / Ubuntu apt-get install libmagic-dev - Red Hat / CentOS / Fedora yum install file-devel or dns install file-devel - Mac OS X (Darwin) brew install libmagic (for Homebrew, see https://brew.sh) port install libmagic (for MacPorts, see https://www.macports.org) - OpenBSD / NetBSD pkg_add file (for pkgsrc, see https://www.pkgsrc.org) - FreeBSD pkg install file (for FreeBSD Ports, see https://www.freebsd.org/ports) Alternatively, you can download recent release of the file(1) package from the following web site and attempt to compile libmagic(3) manually: https://www.darwinsys.com/file EOS end have_library('magic') unless have_func('magic_getpath') abort "\n" + (<<-EOS).gsub(/^[ ]{,3}/, '') + "\n" Your version of libmagic(3) appears to be too old. Please, consider upgrading to at least version 5.29 or newer, if possible. For more information about file(1) command and libmagic(3) please visit the following web site: https://www.darwinsys.com/file EOS end have_func('magic_getflags') %w[ utime.h sys/types.h sys/time.h ].each do |h| have_header(h) end %w[ utime utimes ].each do |f| have_func(f) end create_header create_makefile('magic/magic') if config_clean? # Do not clean if run in a development work tree. File.open('Makefile', 'at') do |mk| mk.print(<<~EOF) all: clean-ports clean-ports: $(DLLIB) \t-$(Q)$(RUBY) $(srcdir)/extconf.rb --clean --#{static_p ? 'enable' : 'disable'}-static EOF end end ruby-magic-0.5.0/ext/magic/functions.c000066400000000000000000000141761412543176100176240ustar00rootroot00000000000000#if defined(__cplusplus) extern "C" { #endif #include "functions.h" static int check_fd(int fd); static int safe_dup(int fd); static int safe_close(int fd); static int safe_cloexec(int fd); static int override_error_output(void *data); static int restore_error_output(void *data); static inline int check_fd(int fd) { errno = 0; if (fd < 0 || (fcntl(fd, F_GETFD) < 0 && errno == EBADF)) { errno = EBADF; return -EBADF; } return 0; } static int safe_dup(int fd) { int new_fd; int local_errno; int flags = F_DUPFD; #if defined(HAVE_F_DUPFD_CLOEXEC) flags = F_DUPFD_CLOEXEC; #endif new_fd = fcntl(fd, flags, fileno(stderr) + 1); if (new_fd < 0 && errno == EINVAL) { new_fd = dup(fd); if (new_fd < 0) { local_errno = errno; goto error; } } if (safe_cloexec(new_fd) < 0) { local_errno = errno; goto error; } return new_fd; error: errno = local_errno; return -1; } static int safe_close(int fd) { int rv; #if defined(HAVE_POSIX_CLOSE_RESTART) rv = posix_close(fd, 0); #else rv = close(fd); if (rv < 0 && errno == EINTR) errno = EINPROGRESS; #endif return rv; } static inline int safe_cloexec(int fd) { int local_errno; int flags = fcntl(fd, F_GETFD); if (flags < 0) { local_errno = errno; goto error; } if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) { local_errno = errno; goto error; } return 0; error: errno = local_errno; return -1; } static int override_error_output(void *data) { int local_errno; int flags = O_WRONLY | O_APPEND; save_t *s = data; #if defined(HAVE_O_CLOEXEC) flags |= O_CLOEXEC; #endif assert(s != NULL && "Must be a valid pointer to `save_t' type"); s->file.old_fd = -1; s->file.new_fd = -1; s->status = -1; fflush(stderr); fgetpos(stderr, &s->file.position); s->file.old_fd = safe_dup(fileno(stderr)); if (s->file.old_fd < 0) { local_errno = errno; goto error; } s->file.new_fd = open("/dev/null", flags, 0222); if (s->file.new_fd < 0) { local_errno = errno; if (dup2(s->file.old_fd, fileno(stderr)) < 0) { local_errno = errno; goto error; } safe_close(s->file.old_fd); goto error; } if (safe_cloexec(s->file.new_fd) < 0) { local_errno = errno; goto error; } if (dup2(s->file.new_fd, fileno(stderr)) < 0) { local_errno = errno; goto error; } safe_close(s->file.new_fd); return 0; error: s->status = local_errno; errno = s->status; return -1; } static int restore_error_output(void *data) { int local_errno; save_t *s = data; assert(s != NULL && "Must be a valid pointer to `save_t' type"); if (s->file.old_fd < 0 && s->status != 0) return -1; fflush(stderr); if (dup2(s->file.old_fd, fileno(stderr)) < 0) { local_errno = errno; goto error; } safe_close(s->file.old_fd); clearerr(stderr); fsetpos(stderr, &s->file.position); if (setvbuf(stderr, NULL, _IONBF, 0) != 0) { local_errno = errno; goto error; } return 0; error: s->status = local_errno; errno = s->status; return -1; } inline magic_t magic_open_wrapper(int flags) { return magic_open(flags); } inline void magic_close_wrapper(magic_t magic) { magic_close(magic); } inline const char* magic_error_wrapper(magic_t magic) { return magic_error(magic); } inline int magic_errno_wrapper(magic_t magic) { return magic_errno(magic); } inline const char* magic_getpath_wrapper(void) { /* * The second argument translates to same value as the * FILE_LOAD constant, which when used results in this * function calling the get_default_magic() internally. * * N.B. magic_getpath() also honors the "MAGIC" * environment variable." */ return magic_getpath(NULL, 0); } inline int magic_getparam_wrapper(magic_t magic, int parameter, void *value) { return magic_getparam(magic, parameter, value); } inline int magic_setparam_wrapper(magic_t magic, int parameter, const void *value) { if (*(const int *)value < 0 || *(const size_t *)value > UINT_MAX) { errno = EOVERFLOW; return -EOVERFLOW; } if (parameter == MAGIC_PARAM_BYTES_MAX) return magic_setparam(magic, parameter, value); if (*(const size_t *)value > USHRT_MAX) { errno = EOVERFLOW; return -EOVERFLOW; } return magic_setparam(magic, parameter, value); } inline int magic_getflags_wrapper(magic_t magic) { #if defined(HAVE_MAGIC_GETFLAGS) return magic_getflags(magic); #else UNUSED(magic); errno = ENOSYS; return -ENOSYS; #endif } inline int magic_setflags_wrapper(magic_t magic, int flags) { if (flags < 0 || flags > 0xfffffff) { errno = EINVAL; return -EINVAL; } #if !defined(HAVE_UTIME) && !defined(HAVE_UTIMES) if (flags & MAGIC_PRESERVE_ATIME) { errno = ENOSYS; return -ENOSYS; } #endif return magic_setflags(magic, flags); } inline int magic_load_wrapper(magic_t magic, const char *magic_file, int flags) { int rv; MAGIC_FUNCTION(magic_load, rv, flags, magic, magic_file); return rv; } inline int magic_load_buffers_wrapper(magic_t magic, void **buffers, size_t *sizes, size_t count, int flags) { int rv; MAGIC_FUNCTION(magic_load_buffers, rv, flags, magic, buffers, sizes, count); return rv; } inline int magic_compile_wrapper(magic_t magic, const char *magic_file, int flags) { int rv; MAGIC_FUNCTION(magic_compile, rv, flags, magic, magic_file); return rv; } inline int magic_check_wrapper(magic_t magic, const char *magic_file, int flags) { int rv; MAGIC_FUNCTION(magic_check, rv, flags, magic, magic_file); return rv; } inline const char* magic_file_wrapper(magic_t magic, const char *filename, int flags) { const char *cstring; MAGIC_FUNCTION(magic_file, cstring, flags, magic, filename); return cstring; } inline const char* magic_buffer_wrapper(magic_t magic, const void *buffer, size_t size, int flags) { const char *cstring; MAGIC_FUNCTION(magic_buffer, cstring, flags, magic, buffer, size); return cstring; } inline const char* magic_descriptor_wrapper(magic_t magic, int fd, int flags) { int local_errno; const char *cstring; if (check_fd(fd) < 0) { local_errno = errno; goto error; } MAGIC_FUNCTION(magic_descriptor, cstring, flags, magic, fd); return cstring; error: errno = local_errno; return NULL; } inline int magic_version_wrapper(void) { return magic_version(); } #if defined(__cplusplus) } #endif ruby-magic-0.5.0/ext/magic/functions.h000066400000000000000000000035461412543176100176300ustar00rootroot00000000000000#if !defined(_FUNCTIONS_H) #define _FUNCTIONS_H 1 #if defined(__cplusplus) extern "C" { #endif #include "common.h" #define MAGIC_FUNCTION(f, r, x, ...) \ do { \ if ((x) & (MAGIC_DEBUG | MAGIC_CHECK)) \ r = f(__VA_ARGS__); \ else { \ save_t __##f; \ override_error_output(&(__##f)); \ r = f(__VA_ARGS__); \ restore_error_output(&(__##f)); \ } \ } while (0) typedef struct file_data { fpos_t position; int old_fd; int new_fd; } file_data_t; typedef struct save { file_data_t file; int status; } save_t; extern magic_t magic_open_wrapper(int flags); extern void magic_close_wrapper(magic_t magic); extern const char *magic_error_wrapper(magic_t magic); extern int magic_errno_wrapper(magic_t magic); extern const char *magic_getpath_wrapper(void); extern int magic_getparam_wrapper(magic_t magic, int parameter, void *value); extern int magic_setparam_wrapper(magic_t magic, int parameter, const void *value); extern int magic_getflags_wrapper(magic_t magic); extern int magic_setflags_wrapper(magic_t magic, int flags); extern int magic_load_wrapper(magic_t magic, const char *magic_file, int flags); extern int magic_load_buffers_wrapper(magic_t magic, void **buffers, size_t *sizes, size_t count, int flags); extern int magic_compile_wrapper(magic_t magic, const char *magic_file, int flags); extern int magic_check_wrapper(magic_t magic, const char *magic_file, int flags); extern const char *magic_file_wrapper(magic_t magic, const char *filename, int flags); extern const char *magic_buffer_wrapper(magic_t magic, const void *buffer, size_t size, int flags); extern const char *magic_descriptor_wrapper(magic_t magic, int fd, int flags); extern int magic_version_wrapper(void); #if defined(__cplusplus) } #endif #endif /* _FUNCTIONS_H */ ruby-magic-0.5.0/ext/magic/ruby-magic.c000066400000000000000000001204421412543176100176450ustar00rootroot00000000000000#if defined(__cplusplus) extern "C" { #endif #include "ruby-magic.h" static int rb_mgc_do_not_auto_load; static int rb_mgc_do_not_stop_on_error; static int rb_mgc_warning; static ID id_at_flags; static ID id_at_paths; static VALUE rb_cMagic; static VALUE rb_mgc_eError; static VALUE rb_mgc_eMagicError; static VALUE rb_mgc_eLibraryError; static VALUE rb_mgc_eNotImplementedError; static VALUE rb_mgc_eParameterError; static VALUE rb_mgc_eFlagsError; static const rb_data_type_t rb_mgc_type; static VALUE magic_get_parameter_internal(void *data); static VALUE magic_set_parameter_internal(void *data); static VALUE magic_get_flags_internal(void *data); static VALUE magic_set_flags_internal(void *data); static VALUE magic_load_internal(void *data); static VALUE magic_load_buffers_internal(void *data); static VALUE magic_compile_internal(void *data); static VALUE magic_check_internal(void *data); static VALUE magic_file_internal(void *data); static VALUE magic_buffer_internal(void *data); static VALUE magic_descriptor_internal(void *data); static VALUE magic_close_internal(void *data); static void *nogvl_magic_load(void *data); static void *nogvl_magic_compile(void *data); static void *nogvl_magic_check(void *data); static void *nogvl_magic_file(void *data); static void *nogvl_magic_descriptor(void *data); static void *magic_library_open(void); static void magic_library_close(void *data); static VALUE magic_allocate(VALUE klass); static void magic_mark(void *data); static void magic_free(void *data); static size_t magic_size(const void *data); #if defined(HAVE_RUBY_GC_COMPACT) static void magic_compact(void *data); #endif static VALUE magic_exception_wrapper(VALUE value); static VALUE magic_exception(void *data); static VALUE magic_library_error(VALUE klass, void *data); static VALUE magic_generic_error(VALUE klass, int magic_errno, const char *magic_error); static VALUE magic_lock(VALUE object, VALUE (*function)(ANYARGS), void *data); static VALUE magic_unlock(VALUE object); static VALUE magic_return(void *data); static int magic_get_flags(VALUE object); static void magic_set_flags(VALUE object, int flags); static VALUE magic_set_paths(VALUE object, VALUE value); /* * call-seq: * Magic.do_not_auto_load -> boolean * * Returns +true+ if the global +do_not_auto_load+ flag is set, or +false+ * otherwise. * * Example: * * Magic.do_not_auto_load #=> false * Magic.do_not_auto_load = true #=> true * Magic.do_not_auto_load #=> true * * See also: Magic::new, Magic#loaded?, Magic#load and Magic#load_buffers */ VALUE rb_mgc_get_do_not_auto_load_global(RB_UNUSED_VAR(VALUE object)) { return CBOOL2RVAL(rb_mgc_do_not_auto_load); } /* * call-seq: * Magic.do_not_auto_load= ( boolean ) -> boolean * * Sets the global +do_not_auto_load+ flag for the Magic object and each of the * Magic object instances. This flag can be used to disable automatic loading of * the Magic database files. * * Returns +true+ if the global +do_not_auto_load+ flag is set, or +false+ * otherwise. * * Example: * * Magic.do_not_auto_load #=> false * Magic.do_not_auto_load = true #=> true * Magic.do_not_auto_load #=> true * * Example: * * Magic.do_not_auto_load = true #=> true * magic = Magic.new * magic.loaded? #=> false * magic.load_buffers(File.read(magic.paths[0] + ".mgc")) #=> nil * magic.loaded? #=> true * * See also: Magic::new, Magic#loaded?, Magic#load and Magic#load_buffers */ VALUE rb_mgc_set_do_not_auto_load_global(RB_UNUSED_VAR(VALUE object), VALUE value) { rb_mgc_do_not_auto_load = RVAL2CBOOL(value); return value; } /* * call-seq: * Magic.do_not_stop_on_error -> boolean * * Returns +true+ if the global +do_not_stop_on_error+ flag is set, or +false+ * otherwise. * * Example: * * Magic.do_not_stop_on_error #=> false * Magic.do_not_stop_on_error = true #=> true * Magic.do_not_stop_on_error #=> true * * See also: Magic::new, Magic::open and Magic#do_not_stop_on_error */ VALUE rb_mgc_get_do_not_stop_on_error_global(RB_UNUSED_VAR(VALUE object)) { return CBOOL2RVAL(rb_mgc_do_not_stop_on_error); } /* * call-seq: * Magic.do_not_stop_on_error= (boolean) -> boolean * * Example: * * Magic.do_not_stop_on_error #=> false * Magic.do_not_stop_on_error = true #=> true * Magic.do_not_stop_on_error #=> true * * See also: Magic::new, Magic::open and Magic#do_not_stop_on_error */ VALUE rb_mgc_set_do_not_stop_on_error_global(RB_UNUSED_VAR(VALUE object), VALUE value) { rb_mgc_do_not_stop_on_error = RVAL2CBOOL(value); return value; } /* * call-seq: * Magic.new -> self * Magic.new( string, ... ) -> self * Magic.new( array ) -> self * * Opens the underlying _Magic_ database and returns a new _Magic_. * * Example: * * magic = Magic.new * magic.class #=> Magic * * See also: Magic::open, Magic::mime, Magic::type, Magic::encoding, Magic::compile and Magic::check */ VALUE rb_mgc_initialize(VALUE object, VALUE arguments) { rb_mgc_object_t *mgc; const char *klass = "Magic"; if (!NIL_P(object)) klass = rb_obj_classname(object); if (rb_block_given_p()) MAGIC_WARNING(0, "%s::new() does not take block; use %s::open() instead", klass, klass); if (RTEST(rb_eval_string("ENV['MAGIC_DO_NOT_STOP_ON_ERROR']"))) rb_mgc_do_not_stop_on_error = 1; if (RTEST(rb_eval_string("ENV['MAGIC_DO_NOT_AUTOLOAD']"))) rb_mgc_do_not_auto_load = 1; MAGIC_OBJECT(object, mgc); mgc->stop_on_errors = 1; if (rb_mgc_do_not_stop_on_error) mgc->stop_on_errors = 0; mgc->mutex = rb_class_new_instance(0, 0, rb_const_get(rb_cObject, rb_intern("Mutex"))); magic_set_flags(object, MAGIC_NONE); magic_set_paths(object, RARRAY_EMPTY); if (rb_mgc_do_not_auto_load) { if (!RARRAY_EMPTY_P(arguments)) MAGIC_WARNING(1, "%s::do_not_auto_load is set; using %s#new() to load " "Magic database from a file will have no effect", klass, klass); return object; } rb_mgc_load(object, arguments); return object; } /* * call-seq: * magic.do_not_stop_on_error -> boolean * * See also: Magic::new, Magic::open and Magic::do_not_stop_on_error */ VALUE rb_mgc_get_do_not_stop_on_error(VALUE object) { rb_mgc_object_t *mgc; MAGIC_CHECK_OPEN(object); MAGIC_OBJECT(object, mgc); return CBOOL2RVAL(!mgc->stop_on_errors); } /* * call-seq: * magic.do_not_stop_on_error= ( boolean ) -> boolean * * See also: Magic::new, Magic::open and Magic::do_not_stop_on_error */ VALUE rb_mgc_set_do_not_stop_on_error(VALUE object, VALUE value) { rb_mgc_object_t *mgc; MAGIC_CHECK_OPEN(object); MAGIC_OBJECT(object, mgc); mgc->stop_on_errors = !RVAL2CBOOL(value); return value; } /* * call-seq: * magic.open? -> true or false * * Returns +true+ if the underlying _Magic_ database is open, * or +false+ otherwise. * * Example: * * magic = Magic.new * magic.open? #=> true * magic.close #=> nil * magic.open? #=> false * * See also: Magic#close?, Magic#close and Magic#new */ VALUE rb_mgc_open_p(VALUE object) { return MAGIC_CLOSED_P(object) ? Qfalse : Qtrue; } /* * call-seq: * magic.close -> nil * * Closes the underlying _Magic_ database. * * Example: * * magic = Magic.new * magic.close #=> nil * * See also: Magic#closed?, Magic#open? and Magic#new */ VALUE rb_mgc_close(VALUE object) { rb_mgc_object_t *mgc; if (MAGIC_CLOSED_P(object)) return Qnil; MAGIC_OBJECT(object, mgc); if (mgc) { MAGIC_SYNCHRONIZED(magic_close_internal, mgc); if (DATA_P(object)) DATA_PTR(object) = NULL; } return Qnil; } /* * call-seq: * magic.closed? -> true or false * * Returns +true+ if the underlying _Magic_ database is closed, * or +false+ otherwise. * * Example: * * magic = Magic.new * magic.closed? #=> false * magic.close #=> nil * magic.closed? #=> true * * See also: Magic#close, Magic#open? and #Magic#new */ VALUE rb_mgc_close_p(VALUE object) { rb_mgc_object_t *mgc; magic_t cookie = NULL; MAGIC_OBJECT(object, mgc); if (mgc) cookie = mgc->cookie; if (DATA_P(object) && cookie) return Qfalse; return Qtrue; } /* * call-seq: * magic.paths -> array * * Example: * * magic = Magic.new * magic.paths #=> ["/etc/magic", "/usr/share/misc/magic"] * */ VALUE rb_mgc_get_paths(VALUE object) { const char *cstring = NULL; VALUE value = Qundef; MAGIC_CHECK_OPEN(object); value = rb_ivar_get(object, id_at_paths); if (!NIL_P(value) && !RARRAY_EMPTY_P(value)) return value; value = rb_funcall(rb_cMagic, rb_intern("default_paths"), 0); if (getenv("MAGIC") || NIL_P(value)) { cstring = magic_getpath_wrapper(); value = magic_split(CSTR2RVAL(cstring), CSTR2RVAL(":")); RB_GC_GUARD(value); } return magic_set_paths(object, value); } /* * call-seq: * magic.get_parameter( integer ) -> integer */ VALUE rb_mgc_get_parameter(VALUE object, VALUE tag) { int local_errno; rb_mgc_object_t *mgc; rb_mgc_arguments_t mga; MAGIC_CHECK_INTEGER_TYPE(tag); MAGIC_CHECK_OPEN(object); MAGIC_OBJECT(object, mgc); mga = (rb_mgc_arguments_t) { .magic_object = mgc, .parameter = { .tag = NUM2INT(tag), }, }; MAGIC_SYNCHRONIZED(magic_get_parameter_internal, &mga); local_errno = errno; if (mga.status < 0) { if (local_errno == EINVAL) MAGIC_GENERIC_ERROR(rb_mgc_eParameterError, local_errno, E_PARAM_INVALID_TYPE); MAGIC_LIBRARY_ERROR(mgc); } return SIZET2NUM(mga.parameter.value); } /* * call-seq: * magic.set_parameter( integer, integer ) -> nil */ VALUE rb_mgc_set_parameter(VALUE object, VALUE tag, VALUE value) { int local_errno; rb_mgc_object_t *mgc; rb_mgc_arguments_t mga; MAGIC_CHECK_INTEGER_TYPE(tag); MAGIC_CHECK_INTEGER_TYPE(value); MAGIC_CHECK_OPEN(object); MAGIC_OBJECT(object, mgc); mga = (rb_mgc_arguments_t) { .magic_object = mgc, .parameter = { .tag = NUM2INT(tag), .value = NUM2SIZET(value), }, }; MAGIC_SYNCHRONIZED(magic_set_parameter_internal, &mga); local_errno = errno; if (mga.status < 0) { switch (local_errno) { case EINVAL: MAGIC_GENERIC_ERROR(rb_mgc_eParameterError, local_errno, E_PARAM_INVALID_TYPE); case EOVERFLOW: MAGIC_GENERIC_ERROR(rb_mgc_eParameterError, local_errno, E_PARAM_INVALID_VALUE); } MAGIC_LIBRARY_ERROR(mgc); } return Qnil; } /* * call-seq: * magic.flags -> integer * * Example: * * magic = Magic.new * magic.flags #=> 0 * magic.flags = Magic::MIME #=> 1040 * magic.flags #=> 1040 * * See also: Magic#flags_to_a */ VALUE rb_mgc_get_flags(VALUE object) { int local_errno; rb_mgc_object_t *mgc; rb_mgc_arguments_t mga; MAGIC_CHECK_OPEN(object); MAGIC_OBJECT(object, mgc); mga = (rb_mgc_arguments_t) { .magic_object = mgc, }; MAGIC_SYNCHRONIZED(magic_get_flags_internal, &mga); local_errno = errno; if (mga.status < 0 && local_errno == ENOSYS) return rb_ivar_get(object, id_at_flags); return INT2NUM(mga.flags); } /* * call-seq: * magic.flags= ( integer ) -> integer * * Example: * * magic = Magic.new * magic.flags = Magic::MIME #=> 1040 * magic.flags = Magic::MIME_TYPE #=> 16 */ VALUE rb_mgc_set_flags(VALUE object, VALUE value) { int local_errno; rb_mgc_object_t *mgc; rb_mgc_arguments_t mga; const char *klass = "Magic"; const char *flag = NULL; MAGIC_CHECK_INTEGER_TYPE(value); MAGIC_CHECK_OPEN(object); MAGIC_OBJECT(object, mgc); mga = (rb_mgc_arguments_t) { .magic_object = mgc, .flags = NUM2INT(value), }; if (mga.flags < 0) MAGIC_GENERIC_ERROR(rb_mgc_eFlagsError, EINVAL, E_FLAG_INVALID_TYPE); if (mga.flags & MAGIC_DEBUG) flag = "DEBUG"; else if (mga.flags & MAGIC_CHECK) flag = "CHECK"; if (flag) { if (!NIL_P(object)) klass = rb_obj_classname(object); MAGIC_WARNING(0, "%s::%s flag is set; verbose information will " "now be printed to the standard error output", klass, flag); } MAGIC_SYNCHRONIZED(magic_set_flags_internal, &mga); local_errno = errno; if (mga.status < 0) { switch (local_errno) { case EINVAL: MAGIC_GENERIC_ERROR(rb_mgc_eFlagsError, local_errno, E_FLAG_INVALID_TYPE); case ENOSYS: MAGIC_GENERIC_ERROR(rb_mgc_eNotImplementedError, local_errno, E_FLAG_NOT_IMPLEMENTED); } MAGIC_LIBRARY_ERROR(mgc); } return rb_ivar_set(object, id_at_flags, INT2NUM(mga.flags)); } /* * call-seq: * magic.load -> nil * magic.load( string, ... ) -> nil * magic.load( array ) -> nil * * Example: * * See also: Magic#check, Magic#compile, Magic::check and Magic::compile */ VALUE rb_mgc_load(VALUE object, VALUE arguments) { rb_mgc_object_t *mgc; rb_mgc_arguments_t mga; const char *klass = "Magic"; VALUE value = Qundef; if (ARRAY_P(RARRAY_FIRST(arguments))) arguments = magic_flatten(arguments); MAGIC_CHECK_ARRAY_OF_STRINGS(arguments); MAGIC_CHECK_OPEN(object); MAGIC_OBJECT(object, mgc); if (rb_mgc_do_not_auto_load) { if (!NIL_P(object)) klass = rb_obj_classname(object); MAGIC_WARNING(2, "%s::do_not_auto_load is set; using %s#load " "will load Magic database from a file", klass, klass); } if (RARRAY_EMPTY_P(arguments)) arguments = rb_mgc_get_paths(object); value = magic_join(arguments, CSTR2RVAL(":")); RB_GC_GUARD(value); magic_set_paths(object, RARRAY_EMPTY); mga = (rb_mgc_arguments_t) { .magic_object = mgc, .file = { .path = RVAL2CSTR(value), }, .flags = magic_get_flags(object), }; MAGIC_SYNCHRONIZED(magic_load_internal, &mga); if (mga.status < 0) { mgc->database_loaded = 0; MAGIC_LIBRARY_ERROR(mgc); } mgc->database_loaded = 1; value = magic_split(CSTR2RVAL(mga.file.path), CSTR2RVAL(":")); RB_GC_GUARD(value); magic_set_paths(object, value); return Qnil; } /* * call-seq: * magic.load_buffers( string, ... ) -> nil * magic.load_buffers( array ) -> nil * * See also: Magic#load and Magic::do_not_auto_load */ VALUE rb_mgc_load_buffers(VALUE object, VALUE arguments) { size_t count; int local_errno; rb_mgc_object_t *mgc; rb_mgc_arguments_t mga; void **pointers = NULL; size_t *sizes = NULL; VALUE value = Qundef; count = (size_t)RARRAY_LEN(arguments); MAGIC_CHECK_ARGUMENT_MISSING(count, 1); if (ARRAY_P(RARRAY_FIRST(arguments))) { arguments = magic_flatten(arguments); count = (size_t)RARRAY_LEN(arguments); } MAGIC_CHECK_ARRAY_EMPTY(arguments); MAGIC_CHECK_ARRAY_OF_STRINGS(arguments); MAGIC_CHECK_OPEN(object); MAGIC_OBJECT(object, mgc); pointers = ALLOC_N(void *, count); if (!pointers) { local_errno = ENOMEM; goto error; } sizes = ALLOC_N(size_t, count); if (!sizes) { ruby_xfree(pointers); local_errno = ENOMEM; goto error; } for (size_t i = 0; i < count; i++) { value = RARRAY_AREF(arguments, (long)i); pointers[i] = (void *)RSTRING_PTR(value); sizes[i] = (size_t)RSTRING_LEN(value); } magic_set_paths(object, RARRAY_EMPTY); mga = (rb_mgc_arguments_t) { .magic_object = mgc, .buffers = { .count = count, .pointers = pointers, .sizes = sizes, }, .flags = magic_get_flags(object), }; MAGIC_SYNCHRONIZED(magic_load_buffers_internal, &mga); if (mga.status < 0) { local_errno = errno; ruby_xfree(pointers); ruby_xfree(sizes); goto error; } mgc->database_loaded = 1; ruby_xfree(pointers); ruby_xfree(sizes); return Qnil; error: mgc->database_loaded = 0; if (local_errno == ENOMEM) MAGIC_GENERIC_ERROR(rb_mgc_eLibraryError, local_errno, E_NOT_ENOUGH_MEMORY); MAGIC_LIBRARY_ERROR(mgc); } /* * call-seq: * magic.loaded? -> true or false * * Returns +true+ if at least a single Magic database file had been loaded, or * +false+ otherwise. Magic database files can be loaded from a file or from an * in-memory buffer. * * Example: * * magic = Magic.new * magic.loaded? #=> true * * Example: * * Magic.do_not_auto_load = true #=> true * magic = Magic.new * magic.loaded? #=> false * * See also: Magic#load and Magic#load_buffers */ VALUE rb_mgc_load_p(VALUE object) { rb_mgc_object_t *mgc; MAGIC_CHECK_OPEN(object); MAGIC_OBJECT(object, mgc); return CBOOL2RVAL(mgc->database_loaded); } /* * call-seq: * magic.compile( string ) -> nil * magic.compile( array ) -> nil * * See also: Magic#check, Magic::check and Magic::compile */ VALUE rb_mgc_compile(VALUE object, VALUE value) { rb_mgc_object_t *mgc; rb_mgc_arguments_t mga; MAGIC_CHECK_STRING_TYPE(value); MAGIC_CHECK_OPEN(object); MAGIC_OBJECT(object, mgc); mga = (rb_mgc_arguments_t) { .magic_object = mgc, .file = { .path = RVAL2CSTR(value), }, .flags = magic_get_flags(object), }; MAGIC_SYNCHRONIZED(magic_compile_internal, &mga); if (mga.status < 0) MAGIC_LIBRARY_ERROR(mgc); return Qnil; } /* * call-seq: * magic.check( string ) -> true or false * magic.check( array ) -> true or false * * See also: Magic#compile, Magic::compile and Magic::check */ VALUE rb_mgc_check(VALUE object, VALUE value) { rb_mgc_object_t *mgc; rb_mgc_arguments_t mga; MAGIC_CHECK_STRING_TYPE(value); MAGIC_CHECK_OPEN(object); MAGIC_OBJECT(object, mgc); mga = (rb_mgc_arguments_t) { .magic_object = mgc, .file = { .path = RVAL2CSTR(value), }, .flags = magic_get_flags(object), }; MAGIC_SYNCHRONIZED(magic_check_internal, &mga); return mga.status < 0 ? Qfalse : Qtrue; } /* * call-seq: * magic.file( object ) -> string or array * magic.file( string ) -> string or array * * See also: Magic#buffer and Magic#descriptor */ VALUE rb_mgc_file(VALUE object, VALUE value) { rb_mgc_object_t *mgc; rb_mgc_arguments_t mga; const char *empty = "(null)"; UNUSED(empty); if (NIL_P(value)) goto error; MAGIC_CHECK_OPEN(object); MAGIC_CHECK_LOADED(object); MAGIC_OBJECT(object, mgc); if (rb_respond_to(value, rb_intern("to_io"))) return rb_mgc_descriptor(object, value); value = magic_path(value); if (NIL_P(value)) goto error; mga = (rb_mgc_arguments_t) { .magic_object = mgc, .file = { .path = RVAL2CSTR(value), }, .flags = magic_get_flags(object), }; MAGIC_SYNCHRONIZED(magic_file_internal, &mga); if (mga.status < 0 && !mga.result) { /* * Handle the case when the "ERROR" flag is set regardless of the * current version of the underlying Magic library. * * Prior to version 5.15 the correct behavior that concerns the * following IEEE 1003.1 standards was broken: * * http://pubs.opengroup.org/onlinepubs/007904975/utilities/file.html * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/file.html * * This is an attempt to mitigate the problem and correct it to achieve * the desired behavior as per the standards. */ if (mgc->stop_on_errors || (mga.flags & MAGIC_ERROR)) MAGIC_LIBRARY_ERROR(mgc); mga.result = magic_error_wrapper(mgc->cookie); } if (!mga.result) MAGIC_GENERIC_ERROR(rb_mgc_eMagicError, EINVAL, E_UNKNOWN); assert(mga.result != NULL && "Must be a valid pointer to `const char' type"); /* * Depending on the version of the underlying Magic library the magic_file() * function can fail and either yield no results or return the "(null)" * string instead. Often this would indicate that an older version of the * Magic library is in use. */ assert(strncmp(mga.result, empty, strlen(empty)) != 0 && "Empty or invalid result"); return magic_return(&mga); error: MAGIC_ARGUMENT_TYPE_ERROR(value, "String or IO-like object"); } /* * call-seq: * magic.buffer( string ) -> string or array * * See also: Magic#file and Magic#descriptor */ VALUE rb_mgc_buffer(VALUE object, VALUE value) { rb_mgc_object_t *mgc; rb_mgc_arguments_t mga; MAGIC_CHECK_STRING_TYPE(value); MAGIC_CHECK_OPEN(object); MAGIC_CHECK_LOADED(object); MAGIC_OBJECT(object, mgc); StringValue(value); mga = (rb_mgc_arguments_t) { .magic_object = mgc, .buffers = { .pointers = (void **)RSTRING_PTR(value), .sizes = (size_t *)RSTRING_LEN(value), }, .flags = magic_get_flags(object), }; MAGIC_SYNCHRONIZED(magic_buffer_internal, &mga); if (mga.status < 0) MAGIC_LIBRARY_ERROR(mgc); assert(mga.result != NULL && "Must be a valid pointer to `const char' type"); return magic_return(&mga); } /* * call-seq: * magic.descriptor( object ) -> string or array * magic.descriptor( integer ) -> string or array * * See also: Magic#file and Magic#buffer */ VALUE rb_mgc_descriptor(VALUE object, VALUE value) { int local_errno; rb_mgc_object_t *mgc; rb_mgc_arguments_t mga; if (rb_respond_to(value, rb_intern("to_io"))) value = INT2NUM(magic_fileno(value)); MAGIC_CHECK_INTEGER_TYPE(value); MAGIC_CHECK_OPEN(object); MAGIC_CHECK_LOADED(object); MAGIC_OBJECT(object, mgc); mga = (rb_mgc_arguments_t) { .magic_object = mgc, .file = { .fd = NUM2INT(value), }, .flags = magic_get_flags(object), }; MAGIC_SYNCHRONIZED(magic_descriptor_internal, &mga); local_errno = errno; if (mga.status < 0) { if (local_errno == EBADF) rb_raise(rb_eIOError, "Bad file descriptor"); MAGIC_LIBRARY_ERROR(mgc); } assert(mga.result != NULL && "Must be a valid pointer to `const char' type"); return magic_return(&mga); } /* * call-seq: * Magic.version -> integer * * Example: * * Magic.version #=> 517 * * See also: Magic::version_to_a and Magic::version_to_s */ VALUE rb_mgc_version(RB_UNUSED_VAR(VALUE object)) { return INT2NUM(magic_version_wrapper()); } static inline void* nogvl_magic_load(void *data) { rb_mgc_arguments_t *mga = data; magic_t cookie = mga->magic_object->cookie; mga->status = magic_load_wrapper(cookie, mga->file.path, mga->flags); return NULL; } static inline void* nogvl_magic_compile(void *data) { rb_mgc_arguments_t *mga = data; magic_t cookie = mga->magic_object->cookie; mga->status = magic_compile_wrapper(cookie, mga->file.path, mga->flags); return NULL; } static inline void* nogvl_magic_check(void *data) { rb_mgc_arguments_t *mga = data; magic_t cookie = mga->magic_object->cookie; mga->status = magic_check_wrapper(cookie, mga->file.path, mga->flags); return NULL; } static inline void* nogvl_magic_file(void *data) { rb_mgc_arguments_t *mga = data; magic_t cookie = mga->magic_object->cookie; mga->result = magic_file_wrapper(cookie, mga->file.path, mga->flags); mga->status = !mga->result ? -1 : 0; return NULL; } static inline void* nogvl_magic_descriptor(void *data) { rb_mgc_arguments_t *mga = data; magic_t cookie = mga->magic_object->cookie; mga->result = magic_descriptor_wrapper(cookie, mga->file.fd, mga->flags); mga->status = !mga->result ? -1 : 0; return NULL; } static inline VALUE magic_get_parameter_internal(void *data) { size_t value; rb_mgc_arguments_t *mga = data; magic_t cookie = mga->magic_object->cookie; mga->status = magic_getparam_wrapper(cookie, mga->parameter.tag, &value); mga->parameter.value = value; return (VALUE)NULL; } static inline VALUE magic_set_parameter_internal(void *data) { size_t value; rb_mgc_arguments_t *mga = data; magic_t cookie = mga->magic_object->cookie; value = mga->parameter.value; mga->status = magic_setparam_wrapper(cookie, mga->parameter.tag, &value); return (VALUE)NULL; } static inline VALUE magic_get_flags_internal(void *data) { int flags; rb_mgc_arguments_t *mga = data; magic_t cookie = mga->magic_object->cookie; mga->status = 0; flags = magic_getflags_wrapper(cookie); if (flags < 0) mga->status = -1; else mga->flags = flags; return (VALUE)NULL; } static inline VALUE magic_set_flags_internal(void *data) { rb_mgc_arguments_t *mga = data; magic_t cookie = mga->magic_object->cookie; mga->status = magic_setflags_wrapper(cookie, mga->flags); return (VALUE)NULL; } static inline VALUE magic_close_internal(void *data) { magic_library_close(data); return Qnil; } static inline VALUE magic_load_internal(void *data) { rb_mgc_arguments_t *mga = data; magic_t cookie = mga->magic_object->cookie; int old_flags = mga->flags; NOGVL(nogvl_magic_load, mga); if (MAGIC_STATUS_CHECK(mga->status < 0)) magic_setflags_wrapper(cookie, old_flags); return (VALUE)NULL; } static inline VALUE magic_load_buffers_internal(void *data) { rb_mgc_arguments_t *mga = data; magic_t cookie = mga->magic_object->cookie; mga->status = magic_load_buffers_wrapper(cookie, mga->buffers.pointers, mga->buffers.sizes, mga->buffers.count, mga->flags); return (VALUE)NULL; } static inline VALUE magic_compile_internal(void *data) { rb_mgc_arguments_t *mga = data; magic_t cookie = mga->magic_object->cookie; int old_flags = mga->flags; NOGVL(nogvl_magic_compile, mga); if (MAGIC_STATUS_CHECK(mga->status < 0)) magic_setflags_wrapper(cookie, old_flags); return (VALUE)NULL; } static inline VALUE magic_check_internal(void *data) { rb_mgc_arguments_t *mga = data; magic_t cookie = mga->magic_object->cookie; int old_flags = mga->flags; NOGVL(nogvl_magic_check, mga); if (MAGIC_STATUS_CHECK(mga->status < 0)) magic_setflags_wrapper(cookie, old_flags); return (VALUE)NULL; } static VALUE magic_file_internal(void *data) { int local_errno; int restore_flags = 0; rb_mgc_arguments_t *mga = data; rb_mgc_object_t *mgc = mga->magic_object; magic_t cookie = mgc->cookie; int old_flags = mga->flags; if (mgc->stop_on_errors) mga->flags |= MAGIC_ERROR; if (mga->flags & MAGIC_CONTINUE) mga->flags |= MAGIC_RAW; if (old_flags != mga->flags) restore_flags = 1; if (restore_flags) magic_setflags_wrapper(cookie, mga->flags); NOGVL(nogvl_magic_file, mga); local_errno = errno; /* * The Magic library often does not correctly report errors, * especially when certain flags (such as e.g., MAGIC_EXTENSION, * etc.) are set. Attempt to obtain an error code first from the * Magic library itself, and if that does not work, then from * the saved errno value. */ if (magic_errno_wrapper(cookie) || local_errno) mga->status = -1; if (restore_flags) magic_setflags_wrapper(cookie, old_flags); return (VALUE)NULL; } static VALUE magic_buffer_internal(void *data) { int restore_flags = 0; rb_mgc_arguments_t *mga = data; magic_t cookie = mga->magic_object->cookie; int old_flags = mga->flags; if (mga->flags & MAGIC_CONTINUE) mga->flags |= MAGIC_RAW; if (old_flags != mga->flags) restore_flags = 1; if (restore_flags) magic_setflags_wrapper(cookie, mga->flags); mga->result = magic_buffer_wrapper(cookie, (const void *)mga->buffers.pointers, (size_t)mga->buffers.sizes, mga->flags); mga->status = !mga->result ? -1 : 0; if (restore_flags) magic_setflags_wrapper(cookie, old_flags); return (VALUE)NULL; } static VALUE magic_descriptor_internal(void *data) { int restore_flags = 0; rb_mgc_arguments_t *mga = data; magic_t cookie = mga->magic_object->cookie; int old_flags = mga->flags; if (mga->flags & MAGIC_CONTINUE) mga->flags |= MAGIC_RAW; if (old_flags != mga->flags) restore_flags = 1; if (restore_flags) magic_setflags_wrapper(cookie, mga->flags); NOGVL(nogvl_magic_descriptor, mga); if (restore_flags) magic_setflags_wrapper(cookie, old_flags); return (VALUE)NULL; } static inline void* magic_library_open(void) { magic_t cookie; cookie = magic_open_wrapper(MAGIC_NONE); if (!cookie) { errno = ENOMEM; return NULL; } return cookie; } static inline void magic_library_close(void *data) { rb_mgc_object_t *mgc = data; assert(mgc != NULL && "Must be a valid pointer to `rb_mgc_object_t' type"); if (mgc->cookie) magic_close_wrapper(mgc->cookie); mgc->cookie = NULL; } static VALUE magic_allocate(VALUE klass) { int local_errno; rb_mgc_object_t *mgc; mgc = RB_ALLOC(rb_mgc_object_t); local_errno = ENOMEM; if (!mgc) { errno = local_errno; MAGIC_GENERIC_ERROR(rb_mgc_eLibraryError, local_errno, E_NOT_ENOUGH_MEMORY); } mgc->cookie = NULL; mgc->mutex = Qundef; mgc->database_loaded = 0; mgc->stop_on_errors = 0; mgc->cookie = magic_library_open(); local_errno = errno; if (!mgc->cookie) { ruby_xfree(mgc); mgc = NULL; errno = local_errno; MAGIC_GENERIC_ERROR(rb_mgc_eLibraryError, local_errno, E_MAGIC_LIBRARY_INITIALIZE); } return TypedData_Wrap_Struct(klass, &rb_mgc_type, mgc); } static inline void magic_mark(void *data) { rb_mgc_object_t *mgc = data; assert(mgc != NULL && "Must be a valid pointer to `rb_mgc_object_t' type"); MAGIC_GC_MARK(mgc->mutex); } static inline void magic_free(void *data) { rb_mgc_object_t *mgc = data; assert(mgc != NULL && "Must be a valid pointer to `rb_mgc_object_t' type"); if (mgc->cookie) magic_library_close(data); mgc->cookie = NULL; mgc->mutex = Qundef; ruby_xfree(mgc); } static inline size_t magic_size(const void *data) { const rb_mgc_object_t *mgc = data; assert(mgc != NULL && "Must be a valid pointer to `rb_mgc_object_t' type"); return sizeof(*mgc); } #if defined(HAVE_RUBY_GC_COMPACT) static inline void magic_compact(void *data) { rb_mgc_object_t *mgc = data; assert(mgc != NULL && "Must be a valid pointer to `rb_mgc_object_t' type"); mgc->mutex = rb_gc_location(mgc->mutex); } #endif /* HAVE_RUBY_GC_COMPACT */ static inline VALUE magic_exception_wrapper(VALUE value) { rb_mgc_error_t *mge = (rb_mgc_error_t *)value; return rb_exc_new2(mge->klass, mge->magic_error); } static VALUE magic_exception(void *data) { int exception = 0; rb_mgc_error_t *mge = data; VALUE object = Qundef; assert(mge != NULL && "Must be a valid pointer to `rb_mgc_error_t' type"); object = rb_protect(magic_exception_wrapper, (VALUE)mge, &exception); if (exception) rb_jump_tag(exception); rb_iv_set(object, "@errno", INT2NUM(mge->magic_errno)); RB_GC_GUARD(object); return object; } static inline VALUE magic_generic_error(VALUE klass, int magic_errno, const char *magic_error) { rb_mgc_error_t mge; mge = (rb_mgc_error_t) { .klass = klass, .magic_errno = magic_errno, .magic_error = magic_error, }; return magic_exception(&mge); } static VALUE magic_library_error(VALUE klass, void *data) { rb_mgc_error_t mge; const char *message = NULL; const char *empty = "(null)"; magic_t cookie = data; UNUSED(empty); assert(cookie != NULL && "Must be a valid pointer to `magic_t' type"); mge = (rb_mgc_error_t) { .klass = klass, .magic_errno = -1, .magic_error = MAGIC_ERRORS(E_UNKNOWN), }; message = magic_error_wrapper(cookie); if (message) { mge.magic_errno = magic_errno_wrapper(cookie); mge.magic_error = message; } assert(strncmp(mge.magic_error, empty, strlen(empty)) != 0 && "Empty or invalid error message"); return magic_exception(&mge); } VALUE magic_lock(VALUE object, VALUE(*function)(ANYARGS), void *data) { rb_mgc_object_t *mgc; MAGIC_OBJECT(object, mgc); rb_funcall(mgc->mutex, rb_intern("lock"), 0); return rb_ensure(function, (VALUE)data, magic_unlock, object); } VALUE magic_unlock(VALUE object) { rb_mgc_object_t *mgc; MAGIC_OBJECT(object, mgc); rb_funcall(mgc->mutex, rb_intern("unlock"), 0); return Qnil; } static VALUE magic_return(void *data) { rb_mgc_arguments_t *mga = data; const char *unknown = "???"; VALUE separator = Qundef; VALUE array, string; string = CSTR2RVAL(mga->result); RB_GC_GUARD(string); /* * The value below is a field separator that can be used to split results * when the CONTINUE flag is set causing all valid matches found by the * Magic library to be returned. */ if (mga->flags & MAGIC_CONTINUE) separator = CSTR2RVAL(MAGIC_CONTINUE_SEPARATOR); if (mga->flags & MAGIC_EXTENSION) { /* * A possible I/O-related error has occurred, and there is very * little sense processing the results, so return string as-is. */ if (mga->status < 0) return string; /* * A number of Magic flags that support primarily files e.g., * MAGIC_EXTENSION, etc., would not return a meaningful value for * directories and special files, and such. Thus, it's better to * return an empty string, to indicate lack of results, rather * than a confusing string consisting of three questions marks. */ if (strncmp(mga->result, unknown, strlen(unknown)) == 0) return CSTR2RVAL(""); separator = CSTR2RVAL(MAGIC_EXTENSION_SEPARATOR); } RB_GC_GUARD(separator); if (mga->flags & (MAGIC_CONTINUE | MAGIC_EXTENSION)) { array = magic_split(string, separator); RB_GC_GUARD(array); if (RARRAY_LEN(array) > 1) return magic_strip_array(array); string = magic_shift(array); } return magic_strip(string); } static inline int magic_get_flags(VALUE object) { return NUM2INT(rb_ivar_get(object, id_at_flags)); } static inline void magic_set_flags(VALUE object, int flags) { rb_ivar_set(object, id_at_flags, INT2NUM(flags)); } static inline VALUE magic_set_paths(VALUE object, VALUE value) { return rb_ivar_set(object, id_at_paths, value); } static const rb_data_type_t rb_mgc_type = { .wrap_struct_name = "magic", .function = { .dmark = magic_mark, .dfree = magic_free, .dsize = magic_size, #if defined(HAVE_RUBY_GC_COMPACT) .dcompact = magic_compact, #endif /* HAVE_RUBY_GC_COMPACT */ }, .flags = RUBY_TYPED_FREE_IMMEDIATELY, }; void Init_magic(void) { id_at_paths = rb_intern("@paths"); id_at_flags = rb_intern("@flags"); rb_cMagic = rb_define_class("Magic", rb_cObject); rb_define_alloc_func(rb_cMagic, magic_allocate); /* * Raised when _Magic_ encounters an error. */ rb_mgc_eError = rb_define_class_under(rb_cMagic, "Error", rb_eStandardError); /* * Stores current value of +errno+ */ rb_define_attr(rb_mgc_eError, "errno", 1, 0); /* * Raised when */ rb_mgc_eMagicError = rb_define_class_under(rb_cMagic, "MagicError", rb_mgc_eError); /* * Raised when */ rb_mgc_eLibraryError = rb_define_class_under(rb_cMagic, "LibraryError", rb_mgc_eError); /* * Raised when */ rb_mgc_eParameterError = rb_define_class_under(rb_cMagic, "ParameterError", rb_mgc_eError); /* * Raised when */ rb_mgc_eFlagsError = rb_define_class_under(rb_cMagic, "FlagsError", rb_mgc_eError); /* * Raised when */ rb_mgc_eNotImplementedError = rb_define_class_under(rb_cMagic, "NotImplementedError", rb_mgc_eError); rb_define_singleton_method(rb_cMagic, "do_not_auto_load", RUBY_METHOD_FUNC(rb_mgc_get_do_not_auto_load_global), 0); rb_define_singleton_method(rb_cMagic, "do_not_auto_load=", RUBY_METHOD_FUNC(rb_mgc_set_do_not_auto_load_global), 1); rb_define_singleton_method(rb_cMagic, "do_not_stop_on_error", RUBY_METHOD_FUNC(rb_mgc_get_do_not_stop_on_error_global), 0); rb_define_singleton_method(rb_cMagic, "do_not_stop_on_error=", RUBY_METHOD_FUNC(rb_mgc_set_do_not_stop_on_error_global), 1); rb_define_singleton_method(rb_cMagic, "version", RUBY_METHOD_FUNC(rb_mgc_version), 0); rb_define_method(rb_cMagic, "initialize", RUBY_METHOD_FUNC(rb_mgc_initialize), -2); rb_define_method(rb_cMagic, "do_not_stop_on_error", RUBY_METHOD_FUNC(rb_mgc_get_do_not_stop_on_error), 0); rb_define_method(rb_cMagic, "do_not_stop_on_error=", RUBY_METHOD_FUNC(rb_mgc_set_do_not_stop_on_error), 1); rb_define_method(rb_cMagic, "open?", RUBY_METHOD_FUNC(rb_mgc_open_p), 0); rb_define_method(rb_cMagic, "close", RUBY_METHOD_FUNC(rb_mgc_close), 0); rb_define_method(rb_cMagic, "closed?", RUBY_METHOD_FUNC(rb_mgc_close_p), 0); rb_define_method(rb_cMagic, "paths", RUBY_METHOD_FUNC(rb_mgc_get_paths), 0); rb_define_method(rb_cMagic, "get_parameter", RUBY_METHOD_FUNC(rb_mgc_get_parameter), 1); rb_define_method(rb_cMagic, "set_parameter", RUBY_METHOD_FUNC(rb_mgc_set_parameter), 2); rb_define_method(rb_cMagic, "flags", RUBY_METHOD_FUNC(rb_mgc_get_flags), 0); rb_define_method(rb_cMagic, "flags=", RUBY_METHOD_FUNC(rb_mgc_set_flags), 1); rb_define_method(rb_cMagic, "file", RUBY_METHOD_FUNC(rb_mgc_file), 1); rb_define_method(rb_cMagic, "buffer", RUBY_METHOD_FUNC(rb_mgc_buffer), 1); rb_define_method(rb_cMagic, "descriptor", RUBY_METHOD_FUNC(rb_mgc_descriptor), 1); rb_alias(rb_cMagic, rb_intern("fd"), rb_intern("descriptor")); rb_define_method(rb_cMagic, "load", RUBY_METHOD_FUNC(rb_mgc_load), -2); rb_define_method(rb_cMagic, "load_buffers", RUBY_METHOD_FUNC(rb_mgc_load_buffers), -2); rb_define_method(rb_cMagic, "loaded?", RUBY_METHOD_FUNC(rb_mgc_load_p), 0); rb_alias(rb_cMagic, rb_intern("load_files"), rb_intern("load")); rb_define_method(rb_cMagic, "compile", RUBY_METHOD_FUNC(rb_mgc_compile), 1); rb_define_method(rb_cMagic, "check", RUBY_METHOD_FUNC(rb_mgc_check), 1); rb_alias(rb_cMagic, rb_intern("valid?"), rb_intern("check")); /* * Controls how many levels of recursion will be followed for * indirect magic entries. */ MAGIC_DEFINE_PARAMETER(INDIR_MAX); /* * Controls the maximum number of calls for name or use magic. */ MAGIC_DEFINE_PARAMETER(NAME_MAX); /* * Controls how many ELF program sections will be processed. */ MAGIC_DEFINE_PARAMETER(ELF_PHNUM_MAX); /* * Controls how many ELF sections will be processed. */ MAGIC_DEFINE_PARAMETER(ELF_SHNUM_MAX); /* * Controls how many ELF notes will be processed. */ MAGIC_DEFINE_PARAMETER(ELF_NOTES_MAX); /* * Controls the length limit for regular expression searches. */ MAGIC_DEFINE_PARAMETER(REGEX_MAX); /* * Controls the maximum number of bytes to read from a file. */ MAGIC_DEFINE_PARAMETER(BYTES_MAX); /* * No special handling and/or flags specified. Default behavior. */ MAGIC_DEFINE_FLAG(NONE); /* * Print debugging messages to standard error output. */ MAGIC_DEFINE_FLAG(DEBUG); /* * If the file queried is a symbolic link, follow it. */ MAGIC_DEFINE_FLAG(SYMLINK); /* * If the file is compressed, unpack it and look at the contents. */ MAGIC_DEFINE_FLAG(COMPRESS); /* * If the file is a block or character special device, then open * the device and try to look at the contents. */ MAGIC_DEFINE_FLAG(DEVICES); /* * Return a MIME type string, instead of a textual description. */ MAGIC_DEFINE_FLAG(MIME_TYPE); /* * Return all matches, not just the first. */ MAGIC_DEFINE_FLAG(CONTINUE); /* * Check the Magic database for consistency and print warnings to * standard error output. */ MAGIC_DEFINE_FLAG(CHECK); /* * Attempt to preserve access time (atime, utime or utimes) of the * file queried on systems that support such system calls. */ MAGIC_DEFINE_FLAG(PRESERVE_ATIME); /* * Do not convert unprintable characters to an octal representation. */ MAGIC_DEFINE_FLAG(RAW); /* * Treat operating system errors while trying to open files and follow * symbolic links as first class errors, instead of storing them in the * Magic library error buffer for retrieval later. */ MAGIC_DEFINE_FLAG(ERROR); /* * Return a MIME encoding, instead of a textual description. */ MAGIC_DEFINE_FLAG(MIME_ENCODING); /* * A shorthand for using MIME_TYPE and MIME_ENCODING flags together. */ MAGIC_DEFINE_FLAG(MIME); /* * Return the Apple creator and type. */ MAGIC_DEFINE_FLAG(APPLE); /* * Do not look for, or inside compressed files. */ MAGIC_DEFINE_FLAG(NO_CHECK_COMPRESS); /* * Do not look for, or inside tar archive files. */ MAGIC_DEFINE_FLAG(NO_CHECK_TAR); /* * Do not consult Magic files. */ MAGIC_DEFINE_FLAG(NO_CHECK_SOFT); /* * Check for EMX application type (only supported on EMX). */ MAGIC_DEFINE_FLAG(NO_CHECK_APPTYPE); /* * Do not check for ELF files (do not examine ELF file details). */ MAGIC_DEFINE_FLAG(NO_CHECK_ELF); /* * Do not check for various types of text files. */ MAGIC_DEFINE_FLAG(NO_CHECK_TEXT); /* * Do not check for CDF files. */ MAGIC_DEFINE_FLAG(NO_CHECK_CDF); /* * Do not check for CSV files. */ MAGIC_DEFINE_FLAG(NO_CHECK_CSV); /* * Do not look for known tokens inside ASCII files. */ MAGIC_DEFINE_FLAG(NO_CHECK_TOKENS); /* * Return a MIME encoding, instead of a textual description. */ MAGIC_DEFINE_FLAG(NO_CHECK_ENCODING); /* * Do not check for JSON files. */ MAGIC_DEFINE_FLAG(NO_CHECK_JSON); /* * Do not use built-in tests; only consult the Magic file. */ MAGIC_DEFINE_FLAG(NO_CHECK_BUILTIN); /* * Do not check for various types of text files, same as NO_CHECK_TEXT. */ MAGIC_DEFINE_FLAG(NO_CHECK_ASCII); /* * Do not look for Fortran sequences inside ASCII files. */ MAGIC_DEFINE_FLAG(NO_CHECK_FORTRAN); /* * Do not look for troff sequences inside ASCII files. */ MAGIC_DEFINE_FLAG(NO_CHECK_TROFF); /* * Return a slash-separated list of extensions for this file type. */ MAGIC_DEFINE_FLAG(EXTENSION); /* * Do not report on compression, only report about the uncompressed data. */ MAGIC_DEFINE_FLAG(COMPRESS_TRANSP); } #if defined(__cplusplus) } #endif ruby-magic-0.5.0/ext/magic/ruby-magic.h000066400000000000000000000227571412543176100176640ustar00rootroot00000000000000#if !defined(_RUBY_MAGIC_H) #define _RUBY_MAGIC_H 1 #if defined(__cplusplus) extern "C" { #endif #include "common.h" #include "functions.h" #define MAGIC_SYNCHRONIZED(f, d) magic_lock(object, (f), (d)) #define MAGIC_OBJECT(o, t) \ TypedData_Get_Struct((o), rb_mgc_object_t, &rb_mgc_type, (t)) #define MAGIC_CLOSED_P(o) RTEST(rb_mgc_close_p((o))) #define MAGIC_LOADED_P(o) RTEST(rb_mgc_load_p((o))) #define MAGIC_WARNING(i, ...) \ do { \ if (!(i) || !(rb_mgc_warning & BIT(i))) { \ rb_mgc_warning |= BIT(i); \ rb_warn(__VA_ARGS__); \ } \ } while (0) #define MAGIC_ERRORS(t) ruby_magic_errors[(t)] #define MAGIC_ARGUMENT_TYPE_ERROR(o, ...) \ rb_raise(rb_eTypeError, MAGIC_ERRORS(E_ARGUMENT_TYPE_INVALID), CLASS_NAME((o)), __VA_ARGS__) #define MAGIC_GENERIC_ERROR(k, e, m) \ rb_exc_raise(magic_generic_error((k), (e), MAGIC_ERRORS(m))) #define MAGIC_LIBRARY_ERROR(c) \ rb_exc_raise(magic_library_error(rb_mgc_eMagicError, (c)->cookie)) #define MAGIC_CHECK_INTEGER_TYPE(o) magic_check_type((o), T_FIXNUM) #define MAGIC_CHECK_STRING_TYPE(o) magic_check_type((o), T_STRING) #define MAGIC_CHECK_ARRAY_OF_STRINGS(o) \ magic_check_type_array_of_strings((o)) #define MAGIC_CHECK_ARGUMENT_MISSING(t, o) \ do { \ if ((t) < (o)) \ rb_raise(rb_eArgError, MAGIC_ERRORS(E_ARGUMENT_MISSING), (t), (o)); \ } while (0) #define MAGIC_CHECK_ARRAY_EMPTY(o) \ do { \ if (RARRAY_EMPTY_P(o)) \ rb_raise(rb_eArgError, "%s", MAGIC_ERRORS(E_ARGUMENT_TYPE_ARRAY_EMPTY)); \ } while (0) #define MAGIC_CHECK_OPEN(o) \ do { \ if (MAGIC_CLOSED_P(o)) \ MAGIC_GENERIC_ERROR(rb_mgc_eLibraryError, EFAULT, \ E_MAGIC_LIBRARY_CLOSED); \ } while (0) #define MAGIC_CHECK_LOADED(o) \ do { \ if (!MAGIC_LOADED_P(o)) \ MAGIC_GENERIC_ERROR(rb_mgc_eMagicError, EFAULT, \ E_MAGIC_LIBRARY_NOT_LOADED); \ } while (0) #define MAGIC_STRINGIFY(s) #s #define MAGIC_DEFINE_FLAG(c) \ rb_define_const(rb_cMagic, MAGIC_STRINGIFY(c), INT2NUM(MAGIC_##c)) #define MAGIC_DEFINE_PARAMETER(c) \ rb_define_const(rb_cMagic, MAGIC_STRINGIFY(PARAM_##c), INT2NUM(MAGIC_PARAM_##c)) enum ruby_magic_error { E_UNKNOWN = 0, E_NOT_ENOUGH_MEMORY, E_ARGUMENT_MISSING, E_ARGUMENT_TYPE_INVALID, E_ARGUMENT_TYPE_UNKNOWN, E_ARGUMENT_TYPE_ARRAY_EMPTY, E_ARGUMENT_TYPE_ARRAY_STRINGS, E_MAGIC_LIBRARY_INITIALIZE, E_MAGIC_LIBRARY_CLOSED, E_MAGIC_LIBRARY_NOT_LOADED, E_PARAM_INVALID_TYPE, E_PARAM_INVALID_VALUE, E_FLAG_NOT_IMPLEMENTED, E_FLAG_INVALID_TYPE }; struct parameter { int tag; size_t value; }; union file { const char *path; int fd; }; struct buffers { size_t count; size_t *sizes; void **pointers; }; typedef struct magic_object { magic_t cookie; VALUE mutex; unsigned int database_loaded:1; unsigned int stop_on_errors:1; } rb_mgc_object_t; typedef struct magic_arguments { rb_mgc_object_t *magic_object; union { struct parameter parameter; union file file; struct buffers buffers; }; const char *result; int status; int flags; } rb_mgc_arguments_t; typedef struct magic_error { const char *magic_error; VALUE klass; int magic_errno; } rb_mgc_error_t; static const char * const ruby_magic_errors[] = { [E_UNKNOWN] = "an unknown error has occurred", [E_NOT_ENOUGH_MEMORY] = "cannot allocate memory", [E_ARGUMENT_MISSING] = "wrong number of arguments (given %d, expected %d)", [E_ARGUMENT_TYPE_INVALID] = "wrong argument type %s (expected %s)", [E_ARGUMENT_TYPE_UNKNOWN] = "unknown type 0x%x (0x%x given)", [E_ARGUMENT_TYPE_ARRAY_EMPTY] = "arguments list cannot be empty (expected array of String)", [E_ARGUMENT_TYPE_ARRAY_STRINGS] = "wrong argument type %s in arguments list (expected String)", [E_MAGIC_LIBRARY_INITIALIZE] = "failed to initialize Magic library", [E_MAGIC_LIBRARY_CLOSED] = "Magic library is not open", [E_MAGIC_LIBRARY_NOT_LOADED] = "Magic library not loaded", [E_PARAM_INVALID_TYPE] = "unknown or invalid parameter specified", [E_PARAM_INVALID_VALUE] = "invalid parameter value specified", [E_FLAG_NOT_IMPLEMENTED] = "flag is not implemented", [E_FLAG_INVALID_TYPE] = "unknown or invalid flag specified", NULL }; #if defined(MAGIC_CUSTOM_CHECK_TYPE) static const char * const magic_ruby_types[] = { "", /* Not an object */ [T_OBJECT] = "Object", [T_CLASS] = "Class", [T_MODULE] = "Module", [T_FLOAT] = "Float", [T_STRING] = "String", [T_REGEXP] = "Regexp", [T_ARRAY] = "Array", [T_HASH] = "Hash", [T_STRUCT] = "Struct", [T_BIGNUM] = "Integer", [T_FILE] = "File", "", /* Internal use */ [T_MATCH] = "MatchData", [T_COMPLEX] = "Complex", [T_RATIONAL] = "Rational", "", /* Internal use */ [T_NIL] = "nil", [T_TRUE] = "true", [T_FALSE] = "false", [T_SYMBOL] = "Symbol", [T_FIXNUM] = "Integer", "", /* Internal use */ NULL }; static const char * magic_ruby_type_name(int type) { const char *name; if (type >= ARRAY_SIZE(magic_ruby_types)) return NULL; name = magic_ruby_types[type]; if (name) return name; return NULL; } static const char * magic_ruby_class_name(VALUE object) { const char *name = NULL; if (NIL_P(object)) name = "nil"; else if (RB_TYPE_P(object, T_TRUE)) name = "true"; else if (RB_TYPE_P(object, T_FALSE)) name = "false"; else name = CLASS_NAME(object); return name; } void magic_check_ruby_type(VALUE object, int type) { const char *name; int object_type = TYPE(object); if (object == Qundef) rb_bug("invalid type leaked to the Ruby space"); if (object_type > T_MASK || object_type == T_DATA) goto error; if (object_type == type) return; name = magic_ruby_type_name(type); if (name) rb_raise(rb_eTypeError, MAGIC_ERRORS(E_ARGUMENT_TYPE_INVALID), magic_ruby_class_name(object), name); error: rb_raise(rb_eTypeError, MAGIC_ERRORS(E_ARGUMENT_TYPE_UNKNOWN), object_type, type); } #endif /* MAGIC_CUSTOM_CHECK_TYPE */ static inline VALUE magic_strip(VALUE v) { return (ARRAY_P(v) || STRING_P(v)) ? rb_funcall(v, rb_intern("strip"), 0) : Qnil; } static inline VALUE magic_shift(VALUE v) { return ARRAY_P(v) ? rb_funcall(v, rb_intern("shift"), 0) : Qnil; } static inline VALUE magic_split(VALUE a, VALUE b) { return (STRING_P(a) && STRING_P(b)) ? rb_funcall(a, rb_intern("split"), 1, b) : Qnil; } static inline VALUE magic_join(VALUE a, VALUE b) { return (ARRAY_P(a) && STRING_P(b)) ? rb_funcall(a, rb_intern("join"), 1, b) : Qnil; } static inline VALUE magic_flatten(VALUE v) { return ARRAY_P(v) ? rb_funcall(v, rb_intern("flatten"), 0) : Qnil; } static inline VALUE magic_strip_array(VALUE value) { VALUE entry; VALUE array = rb_ary_new(); for (int i = 0; i < RARRAY_LEN(value); i++) { entry = rb_ary_entry(value, i); if (NIL_P(entry)) continue; if (STRING_P(entry)) { if (RSTRING_EMPTY_P(entry)) continue; entry = magic_strip(entry); } rb_ary_push(array, entry); } return array; } static int magic_fileno(VALUE object) { int fd; rb_io_t *io; if (rb_respond_to(object, rb_intern("fileno"))) { object = rb_funcall(object, rb_intern("fileno"), 0); return NUM2INT(object); } if (!FILE_P(object)) object = rb_convert_type(object, T_FILE, "IO", "to_io"); GetOpenFile(object, io); fd = FPTR_TO_FD(io); if (fd < 0) rb_raise(rb_eIOError, "closed stream"); return fd; } static inline VALUE magic_path(VALUE object) { if (STRING_P(object)) return object; if (rb_respond_to(object, rb_intern("to_path"))) return rb_funcall(object, rb_intern("to_path"), 0); if (rb_respond_to(object, rb_intern("path"))) return rb_funcall(object, rb_intern("path"), 0); if (rb_respond_to(object, rb_intern("to_s"))) return rb_funcall(object, rb_intern("to_s"), 0); return Qnil; } static inline void magic_check_type(VALUE object, RVALUE_TYPE type) { VALUE boolean; boolean = rb_obj_is_kind_of(object, T_INTEGER); if (type == T_FIXNUM && !RVAL2CBOOL(boolean)) MAGIC_ARGUMENT_TYPE_ERROR(object, rb_class2name(T_INTEGER)); MAGIC_CHECK_RUBY_TYPE(object, type); } static inline void magic_check_type_array_of_strings(VALUE object) { VALUE value; for (int i = 0; i < RARRAY_LEN(object); i++) { value = RARRAY_AREF(object, (long)i); if (NIL_P(value) || !STRING_P(value)) rb_raise(rb_eTypeError, MAGIC_ERRORS(E_ARGUMENT_TYPE_ARRAY_STRINGS), CLASS_NAME(value)); } } void Init_magic(void); VALUE rb_mgc_get_do_not_auto_load_global(VALUE object); VALUE rb_mgc_set_do_not_auto_load_global(VALUE object, VALUE value); VALUE rb_mgc_get_do_not_stop_on_error_global(VALUE object); VALUE rb_mgc_set_do_not_stop_on_error_global(VALUE object, VALUE value); VALUE rb_mgc_initialize(VALUE object, VALUE arguments); VALUE rb_mgc_get_do_not_stop_on_error(VALUE object); VALUE rb_mgc_set_do_not_stop_on_error(VALUE object, VALUE value); VALUE rb_mgc_open_p(VALUE object); VALUE rb_mgc_close(VALUE object); VALUE rb_mgc_close_p(VALUE object); VALUE rb_mgc_get_paths(VALUE object); VALUE rb_mgc_get_parameter(VALUE object, VALUE tag); VALUE rb_mgc_set_parameter(VALUE object, VALUE tag, VALUE value); VALUE rb_mgc_get_flags(VALUE object); VALUE rb_mgc_set_flags(VALUE object, VALUE value); VALUE rb_mgc_load(VALUE object, VALUE arguments); VALUE rb_mgc_load_buffers(VALUE object, VALUE arguments); VALUE rb_mgc_load_p(VALUE object); VALUE rb_mgc_compile(VALUE object, VALUE arguments); VALUE rb_mgc_check(VALUE object, VALUE arguments); VALUE rb_mgc_file(VALUE object, VALUE value); VALUE rb_mgc_buffer(VALUE object, VALUE value); VALUE rb_mgc_descriptor(VALUE object, VALUE value); VALUE rb_mgc_version(VALUE object); #if defined(__cplusplus) } #endif #endif /* _RUBY_MAGIC_H */ ruby-magic-0.5.0/kwilczynski-public.pem000066400000000000000000000027251412543176100201250ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIEIDCCAoigAwIBAgIBATANBgkqhkiG9w0BAQsFADAdMRswGQYDVQQDDBJrdy9E Qz1saW51eC9EQz1jb20wHhcNMjEwMTAxMDAwOTU0WhcNMjYwMTAxMDAwOTU0WjAd MRswGQYDVQQDDBJrdy9EQz1saW51eC9EQz1jb20wggGiMA0GCSqGSIb3DQEBAQUA A4IBjwAwggGKAoIBgQCv02+pOrAMVUipLY7qjNLx8OMfMjnVCBH21SsWn8gyIHE9 BtQ+tlsJU8nWNjlr++7SKIV5vQmyYsC2UY0cdcFASdoV0A3Lc2Z0makfvPgcIlpL 5THqQtP11tJfhjQ3MSFJYRG2D6m6GNqA+WTTsvJbPIxy5PHv5xb+79LLzcVxB9Op C0ervTOF8W6Ye4EDvhQ5tppOr/d26UQV+8zRuxjxx5uZSPfUrE9u/gIIdeROoB3T cnjAfzvjIzampLQppvhUnT4Vf5Rum2LohZ5GosKLy7tM65vr1eLyI2ndvS1MXWrU 05sFKm5CSqORD+TVkR1jd3z5HdPgO1KwadccxFixoI8kEIt/iixWTzbka2T2iJoq 5DeNMEMYYAZKuAGBzBcgvgPccCeAXFVF9AHjfaGRUMmYNHKK2HD6AdgNdhcE18V+ CJxFtggCJFYc1ygFOsHZPTbKo9+3ybSQhAswtDosWt26utUT1JOc2Bkj1sgQ0Yrk orANsdYz2ee3JFkzx08CAwEAAaNrMGkwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAw HQYDVR0OBBYEFME41mMBb/XAHgjhlX4kE8xnXxewMBcGA1UdEQQQMA6BDGt3QGxp bnV4LmNvbTAXBgNVHRIEEDAOgQxrd0BsaW51eC5jb20wDQYJKoZIhvcNAQELBQAD ggGBAF/2ryrSgTpf0+wYG46Oi6V3p9A3uMxxbGsh0ZmlLQT1Z1ozclR+xzxzyIFC i7YWYyR085brDBnYNXgnUT9Nm4iAYx58MaPD28IFrcX0XA3DJE6Wkx4MmMV0chiJ kf5ASikfGtFtf9qDmpLQB4louB6HpKNq1MwsQo8zwY93t77HCiV5f0g43rcZfGTn wfBhpT7oOVWgfTLeBD811aS52VUCWclmnz+f01GdTeMwqsgc6oJaPSvg4JCMN94O wcBlxus69rhM8PeTJNhwLxzRCNnZ69t9k2cU+4lTmQQIB8gnEFJxY9gZdgQ6pej0 HXUIut+6pp5BRYF2mTZ3fnxhWyIZaeB7lla2gI6rV2GXDbp4kVplGEuQ975YklJw i4y7bz2Es4Hkly2Nc/J2L0+uMoBhMWMY3Xl0XDdkZYtBdnSB5g7NacChaAsukEkd Ozt6vfXxSzW/KfodB+0ELjlHWiFbI8w7c8Kt7/ceZGVsXTxD/f4tVWy8MmxFy3pY Cpe8Kg== -----END CERTIFICATE----- ruby-magic-0.5.0/kwilczynski.asc000066400000000000000000000523511412543176100166360ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- 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 wd0LNE7PLbmU =0Z34 -----END PGP PUBLIC KEY BLOCK----- ruby-magic-0.5.0/lib/000077500000000000000000000000001412543176100143255ustar00rootroot00000000000000ruby-magic-0.5.0/lib/magic.rb000066400000000000000000000117261412543176100157410ustar00rootroot00000000000000# frozen_string_literal: true begin ::RUBY_VERSION =~ /(\d+\.\d+)/ require_relative "magic/#{Regexp.last_match(1)}/magic" rescue LoadError require_relative 'magic/magic' end require_relative 'magic/version' require_relative 'magic/core/file' require_relative 'magic/core/string' # # File _Magic_ in Ruby. # # Simple interface to _libmagic_ for Ruby Programming Language. # class Magic # # call-seq: # magic.inspect -> string # # Example: # # magic = Magic.new # magic.inspect #=> "#" # magic.close # magic.inspect #=> "#" # def inspect super.insert(-2, self.closed? ? ' (closed)' : '') end # # call-seq: # magic.flags_list( boolean ) -> array # # See also: Magic#flags and Magic#flags_names # def flags_list(names = false) raise LibraryError, 'Magic library is not open' if closed? return [names ? 'NONE' : 0] if @flags.zero? n, i = 0, @flags flags = [] @@flags_map ||= flags_as_map if names while i > 0 n = 2 ** (Math.log(i) / Math.log(2)).to_i i = i - n flags.insert(0, names ? @@flags_map[n] : n) end flags end alias_method :flags_to_a, :flags_list # call-seq: # magic.flags_names -> array # # See also: Magic#flags and Magic#flags_list # def flags_names flags_list(true) end class << self # # call-seq: # Magic.open( integer ) -> self # Magic.open( integer ) {|magic| block } -> string or array # # See also: Magic::mime, Magic::type, Magic::encoding, Magic::compile and Magic::check # def open(flags = Magic::NONE) magic = Magic.new magic.flags = flags if block_given? begin yield magic ensure magic.close end else magic end end # # call-seq: # Magic.mime -> self # Magic.mime {|magic| block } -> string or array # # See also: Magic::open, Magic::type, Magic::encoding, Magic::compile and Magic::check # def mime(&block) open(Magic::MIME, &block) end alias_method :mime_type, :mime # # call-seq: # Magic.type -> self # Magic.type {|magic| block } -> string or array # # See also: Magic::open, Magic::mime, Magic::encoding, Magic::compile and Magic::check # def type(&block) open(Magic::MIME_TYPE, &block) end # # call-seq: # Magic.encoding -> self # Magic.encoding {|magic| block } -> string or array # # See also: Magic::open, Magic::mime, Magic::type, Magic::compile and Magic::check # def encoding(&block) open(Magic::MIME_ENCODING, &block) end # # call-seq: # Magic.compile( string, ... ) -> true # Magic.compile( array ) -> true # # See also: Magic::open, Magic::mime, Magic::type, Magic::encoding, and Magic::check # def compile(*paths) open {|m| m.compile(paths) } end # # call-seq: # Magic.check( string, ... ) -> true or false # Magic.check( array ) -> true or false # # See also: Magic::open, Magic::mime, Magic::type, Magic::encoding and Magic::compile # def check(*paths) open {|m| m.check(paths) } end # # call-seq: # Magic.file( object ) -> string or array # Magic.file( string ) -> string or array # Magic.file( string, integer ) -> string or array # # See also: # def file(path, flags = Magic::MIME) open(flags).file(path) end # # call-seq: # Magic.buffer( string ) -> string or array # Magic.buffer( string, integer ) -> string or array # # See also: # def buffer(buffer, flags = Magic::MIME) open(flags).buffer(buffer) end # # call-seq: # Magic.descriptor( object ) -> string or array # Magic.descriptor( integer ) -> string or array # Magic.descriptor( integer, integer ) -> string or array # # See also: # def descriptor(fd, flags = Magic::MIME) open(flags).descriptor(fd) end alias_method :fd, :descriptor private def default_paths paths = Dir.glob(File.expand_path(File.join(File.dirname(__FILE__), "../ext/magic/share/*.mgc"))) paths.empty? ? nil : paths end end private def power_of_two?(number) number > 0 && Math.log2(number) % 1 == 0 end def flags_as_map klass = self.class klass.constants.each_with_object({}) do |constant, flags| constant = constant.to_s next if constant.start_with?('PARAM_') value = klass.const_get(constant) if value.is_a?(Integer) && power_of_two?(value) flags[value] = constant end end end end FileMagic = Magic ruby-magic-0.5.0/lib/magic/000077500000000000000000000000001412543176100154055ustar00rootroot00000000000000ruby-magic-0.5.0/lib/magic/core/000077500000000000000000000000001412543176100163355ustar00rootroot00000000000000ruby-magic-0.5.0/lib/magic/core/file.rb000066400000000000000000000021751412543176100176060ustar00rootroot00000000000000# frozen_string_literal: true class File class << self # # call-seq: # File.magic( object ) -> string or array # File.magic( string ) -> string or array # # See also: File::mime and File::type # def magic(path, flags = Magic::NONE) Magic.open(flags) {|mgc| mgc.file(path) } end # # call-seq: # File.mime( string ) -> string or array # # See also: File::magic and File::type # def mime(path) magic(path, Magic::MIME) end # # call-seq: # File.type( string ) -> string or array # # See also: File::magic and File::mime # def type(path) magic(path, Magic::MIME_TYPE) end end # # call-seq: # File.magic -> string or array # # See also: File#mime and File#type # def magic self.class.magic(self) end # # call-seq: # File.mime -> string or array # # See also: File#magic and File#type # def mime self.class.mime(self) end # # call-seq: # File.type -> string or array # # See also: File#magic and File#mime # def type self.class.type(self) end end ruby-magic-0.5.0/lib/magic/core/string.rb000066400000000000000000000010371412543176100201710ustar00rootroot00000000000000# frozen_string_literal: true class String # # call-seq: # string.magic -> string or array # # See also: String#mime and String#type # def magic(flags = Magic::NONE) Magic.open(flags) {|mgc| mgc.buffer(self) } end # # call-seq: # string.mime -> string or array # # See also: String#magic and String#type # def mime magic(Magic::MIME) end # # call-seq: # string.type -> string or array # # See also: String#magic and String#mime # def type magic(Magic::MIME_TYPE) end end ruby-magic-0.5.0/lib/magic/version.rb000066400000000000000000000014071412543176100174210ustar00rootroot00000000000000# frozen_string_literal: true class Magic # # Current version of _Magic_. # VERSION = '0.5.0'.freeze class << self # # call-seq: # Magic.version_array -> array # # Example: # # Magic.version_array #=> [5, 39] # # See also: Magic::version and Magic::version_string # def version_array [self.version / 100, self.version % 100] end alias_method :version_to_a, :version_array # # call-seq: # Magic.version_string -> string # # Example: # # Magic.version_string #=> "5.39" # # See also: Magic::version and Magic::version_array # def version_string '%d.%02d' % self.version_array end alias_method :version_to_s, :version_string end end ruby-magic-0.5.0/patches/000077500000000000000000000000001412543176100152065ustar00rootroot00000000000000ruby-magic-0.5.0/patches/libmagic/000077500000000000000000000000001412543176100167555ustar00rootroot000000000000000001-Don-t-attempt-to-build-tests-documentation-and-Python-bindings.patch000066400000000000000000000024131412543176100342700ustar00rootroot00000000000000ruby-magic-0.5.0/patches/libmagicFrom 0f2752ad4d7ea65c80d76493899aca67e90be524 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= Date: Mon, 5 Apr 2021 16:51:59 +0000 Subject: [PATCH] Don't attempt to build tests, documentation and Python bindings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Krzysztof Wilczyński --- Makefile.am | 2 +- Makefile.in | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile.am b/Makefile.am index 2ab67ed..c7fafeb 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,7 +2,7 @@ ACLOCAL_AMFLAGS = -I m4 EXTRA_DIST = MAINT -SUBDIRS = src magic tests doc python +SUBDIRS = src magic # This variable must have 'exec' in its name, in order to be installed # by 'install-exec' target (instead of default 'install-data') diff --git a/Makefile.in b/Makefile.in index e19b15e..dcb5ce6 100644 --- a/Makefile.in +++ b/Makefile.in @@ -358,7 +358,7 @@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ ACLOCAL_AMFLAGS = -I m4 EXTRA_DIST = MAINT -SUBDIRS = src magic tests doc python +SUBDIRS = src magic # This variable must have 'exec' in its name, in order to be installed # by 'install-exec' target (instead of default 'install-data') -- 2.31.0 ruby-magic-0.5.0/ruby-magic.gemspec000066400000000000000000000034231412543176100171650ustar00rootroot00000000000000signing_key = File.expand_path('~/.gem/kwilczynski-private.pem') require 'yaml' dependencies = YAML.load_file(File.join(File.dirname(__FILE__), "dependencies.yml")) Gem::Specification.new do |s| s.name = 'ruby-magic' s.summary = 'File Magic in Ruby' s.description = (<<-EOS).gsub(/^[ ]+/, '') File Magic in Ruby. Simple interface to libmagic for Ruby Programming Language. EOS s.post_install_message = (<<-EOS).gsub(/^[ ]+/, '') Thank you for installing! EOS s.platform = Gem::Platform::RUBY s.version = File.read('VERSION').strip s.license = 'Apache-2.0' s.author = 'Krzysztof Wilczyński' s.email = 'kw@linux.com' s.homepage = 'https://github.com/kwilczynski/ruby-magic' s.required_ruby_version = '>= 2.5.0' s.rubygems_version = '>= 3.1.0' s.metadata = { 'bug_tracker_uri' => 'https://github.com/kwilczynski/ruby-magic/issues', 'source_code_uri' => 'https://github.com/kwilczynski/ruby-magic', 'changelog_uri' => 'https://github.com/kwilczynski/ruby-magic/blob/master/CHANGELOG.md', 'documentation_uri' => 'https://www.rubydoc.info/gems/ruby-magic', 'wiki_uri' => 'https://github.com/kwilczynski/ruby-magic/wiki' } s.files = Dir['ext/**/*.{c,h,rb}'] + Dir['lib/**/*.rb'] + %w( AUTHORS CHANGELOG.md CONTRIBUTORS.md COPYRIGHT LICENSE NOTICE README.md dependencies.yml kwilczynski-public.pem ) s.require_paths << 'lib' s.extensions << 'ext/magic/extconf.rb' s.cert_chain = [ 'kwilczynski-public.pem' ] s.signing_key = signing_key if File.exist?(signing_key) s.add_runtime_dependency("mini_portile2", "~> 2.6") # keep version in sync with extconf.rb end ruby-magic-0.5.0/test/000077500000000000000000000000001412543176100145365ustar00rootroot00000000000000ruby-magic-0.5.0/test/fixtures/000077500000000000000000000000001412543176100164075ustar00rootroot00000000000000ruby-magic-0.5.0/test/fixtures/invalid.magic000066400000000000000000000000301412543176100210300ustar00rootroot00000000000000Not a valid Magic file! ruby-magic-0.5.0/test/fixtures/png-broken.magic000066400000000000000000000004661412543176100214610ustar00rootroot00000000000000>0 string \x89PNG\x0d\x0a\x1a\x0a PNG image data !:mime image/png >16 belong x \b, %d x >20 belong x %d, >24 byte x %d-bit >25 byte 0 grayscale, >25 byte 2 \b/color RGB, >25 byte 3 colormap, >25 byte 4 gray+alpha, >25 byte 6 \b/color RGBA, >28 byte 0 non-interlaced >28 byte 1 interlaced ruby-magic-0.5.0/test/fixtures/png-fake.magic000066400000000000000000000004741412543176100211060ustar00rootroot000000000000000 string \x89PNG\x0d\x0a\x1a\x0a Ruby Gem image !:mime image/x-ruby-gem >16 belong x \b, %d x >20 belong x %d, >24 byte x %d-bit >25 byte 0 grayscale, >25 byte 2 \b/color RGB, >25 byte 3 colormap, >25 byte 4 gray+alpha, >25 byte 6 \b/color RGBA, >28 byte 0 non-interlaced >28 byte 1 interlaced ruby-magic-0.5.0/test/fixtures/png.magic000066400000000000000000000004651412543176100202020ustar00rootroot000000000000000 string \x89PNG\x0d\x0a\x1a\x0a PNG image data !:mime image/png >16 belong x \b, %d x >20 belong x %d, >24 byte x %d-bit >25 byte 0 grayscale, >25 byte 2 \b/color RGB, >25 byte 3 colormap, >25 byte 4 gray+alpha, >25 byte 6 \b/color RGBA, >28 byte 0 non-interlaced >28 byte 1 interlaced ruby-magic-0.5.0/test/fixtures/ruby.jpg000066400000000000000000001013401412543176100200710ustar00rootroot00000000000000JFIFC   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" @G#Z3a6ai\lw}װodo:;CK{SC:7t,xnwl|t>>O=_Gqt;;X{+~?Ioap6Z9||:5̣,e#Z\0`\PAg+>'z<6r!_3kеWK2eX-V"[ٺ-^_ę6.77G a3cf`"%.0a8^к7//UX-*ԥBl-n5}'y,y<{{r]uG-Be%.!q 1v.B޻غ`|.z&/zp lĶQ-JTl-wc~h_7EƶŚ8-YXY[o mr۴JJlKn4jXV؋ay_cmqL{Ow넠Xa oQs.wp|^{ @@(iBԱRAl-_׼{O>j׷ڱu|Xmﻯ )~]XXb =[gG_c,X(@((UrAl}7<\,qÍ&[ӌtӊ-X}i%b" q8n TQԠ( RJ,TݧWg~ LІ ,QԠ(-JT{_U]S};ŶՐTAR&R * \xӕ!ZHux 'ƹݣO`( ,QԠ(-Mj?=qF2U!PYHT!RHJjÑo.Gy篿}/(V X,@B*P^g>#7B +TR ,v;9OWaWi>0L@ (Yܳ<K߼۱l #&"A,DĤĤȱRl~л2H<y?lsz"[`YV %X(PP`HjQ_ֺ|:p:׏~>_cXT%`EEB),@DoOF4t\vtO>2ؖ-m@T'No);zE8y5WoBAB `,,@,,@ =<׺| |'|ÛԷж amؖؖ( @PS=; c-p<9Ʋ%$%%XM=Oͽ'g z~mNjߩfVe qlE-ĶDT(jw|G_2q&yn$^[\E#{fLlX@bX"Ug͆|<|3GvަXjUb[qlEն%% %P}:|. 8N3 /&qq伇hߥ@+$@XR$ ھP3ͻGZa 7~XjUb[qn6-ƭۍ[b( @rףR9[S5۱miZ٨mjzw/3@ER A* ݾqaəyG~ohviv o㥞jXVZ-[qR~G NzckP6 P P]q9A E g1/ʀ<aӯ~c \4 xJTRJ,[Z[qۍ*Y@_n}~mr~&L E5 P6Cl>H,  J! GaNz^ho ӆ2N\ta7UbZjXKRS2vz|:ynnO&[֡mᵨfmᵨ^;   m7`;_TuuuG;Fx:y1~t^Xv:p6aa)RJ*ԱRNpy~4:|x8)rb2TA`TAx\4 !@@ V m {8Ǘ񾆬sy9^~wp4a\7Fopӆ.:f,%*XVC}w_O_G'tptsề <a\N :f.2Wt?NW˔=lOf**  * Nug ` L][-* o^'w/:~?6s?Tx3gG;F:90юv7qu5̴a ӆn<|{Gu枃r=-Yy!j , * * ~Ae2B]DNCkoIz|~Z;tis>}}Sݫ7ecV+q8Ҽ~_֬6\KL5.HZ q11AP][03" * l-2UOuxo>п_.C@2.+ʜ\f\Ēg*q1*qqrn6taۘ$;.H,P ,̥,*͒aXCwzwg1iɎ'q7Ë.V<\f\x̹S%ceʜ\eceʜ\WLXqr/lӁIQɈ RZXYBdxl (2d vCql*۽7=(zOowdů..Xe:yXoKNj&\xNj&\x̹Sx^F:#-[7r( e-Eז9&@5՞\ I dG|ۚ-޺pK;czsCo}NnfS~~K0 zvNƸ&l˛\]l˛\6|2"Y *P $ B,X;f;e@ ٯ%ZöZ_b|^WqߑWz2 "}_Zmr65vL!YvMeͮ& ݸ!  $  le-FHم%W w2S/yR,`|I^y1Lb1LFL m{7kK(&0H( e`d6*Z V-4Ѭ]` n^>zD@ He>Ӊճ9r`L9b2ѳ㍸"X`%J@ͶUtKlO;M@'~sӖ=?op}(LA@g\21cLLaF_?3H"X ,ia@`CezUURUT%SO뺸+2@2?a{OaOq^O9( [Gq=#,xXRI:r&0,rH"X b 6l͹\rUYKVURT%U-%XՋIh,KjN7.z?{l]}?V<]ғW#C H$K@Le,HN n=}97|7pn>/=˕p]¹.9y>'=K,,cu?Uk7}>*/?w..;:Ǜ<Ψ|=pǞʺ/?yqnO1y>y#.=3ڗ p(/@P 0!"1#$23%4`55[|urAey>,}Xܱc%K}>(}P7kQf ۨt `‚ ޅ)1ۉqj}Y+u+uN7BIޙqqqy'l$3e\gir{݈{U&A^∌.z)>>Wl+u/Owe u^2W+Q\Iӣ},In#nO[ѥ ґ/KVݐ5#ݨ礓{]q-S8\1cچI{k^ TZWҥjP7✹--j7pԯ]FZau<Ej':ZQتl/O{'RNĸ1U.C-%jT9cQq\ZsIV+~_/ @v,zu ߦ`}[V/N4ROеQs%P".d졆v|,ލO/Kyl4B`d2 CT7iSՓm͐RR#]C!d+H-(c;$pJ^Gd2 촭8+IO#2"M Sd2 C!# /R{8%/bLh)fd2 C!VrtZޟQiPeS%@sd2 C!d&q4Vx5+RȬS\\\~‹p"ƕ^7FwKߤi5{I~B}ߥ.'/+ Gb.DUw g~^UpITIF Q%5[[QX)~5T˃˒[P*K[|}I,^5 \ m>n|_...//G}@NUo̓ d JZC-ɐNn___/)iSƭh dvK:MFKrSo7QU7T/ d 79N "5mJw t'P d =2-!Jz!6}˛ci"/n d 2@$WM5Viu:_4mc0w2@ d =ӨKp0l7ҭT(ȟM}0x8d 2@ ~}*D"Xz$J9cF@ d 2@ O1ƱVǀVi$f3sbCX( d 2@za84"UOX<1R_bO4h3 d 2@ ea)Eo*wd/ͤJfGy+ d 2@tb%B46#\j\_zROZ?€LZdaq  mKLHPM fYqqqd2 Y;UӴ SݙC!d2 C!f?sڗ 9U:zC!d2 C!d2 C!=h%J>B+3 C!d2 C!d2 C!Us/cq}w?J2T)?S!d2 C!d2 C!dR[KuzCt , C!d2 C!d2 C!PrԿ^\?gbFdq3d2 C!d2./GL}u!s֖!d2\:\_7?v/(д$c%x\\_Q ε㱚#)?pS y2۞<֯R󊼚QΗR+JR;錼\?ǝG`aYVH>w_^-?ҞYNww_>;E2֐W+^;<I1](Ezg}l:ER}/xNN3}m?H*M.p´jY< lԜep Jj>ERr%-|4WU7{-i4cP|5 ]g=fW#D^N/ߠ[[/8ƾf.u&?#XȜy7IA%dq8xp32Rɿi7Z' I4JWKMN•F31FiĸmCOR=8I)=_63Pw;;C@ dZKKGzkT߫B~.j3P8>+T`HIhwYiu/U^dji~I&a1PL j 0$ L0Zu8``` " A d$ÌM  DCW] paJPM.ig* łӉ ӱP!*$JA0b $oRgPX'tMbHP*l 0T9-8-6A:u?hp&i 3).5I%$g:(f,i3q%-:N0Zu? M6ĎM\Dt9A:AZ~@:tYTGT) OHV N'OGAA4h$ aEH&[H"4 !01@A#2BQ"Raq3Cb?Dֵ5MCچxX~м$`^/ p`N}9ҤX9 =3L,&8NѸwvRh7EBSJiM):veWGoD8IfwzkFbl͵h݆}SJiM*,NΖ4zBGo<<\x{HʊPI=\-5 \x/…9| Q}.>xlu>6T*<[h]4QXVgEMppP,cq͌4Z(:IelMI}TpXV+?Nx<Ś$&ɋEjZVmUWmsxHtr؛s2c(lBQQQS1yɍ8AcR֪*******p3vV`q8øsPSrdmZEL訨hƑ İWNjy&0QTTTTT81F<)9I+crb.,?Ҁ G᳞8L\ A lkY{W,YMCyieF(P@P9LIJ.jlKeI;li3G敪X&U&̩qQŽgI҇2у?WGky&btZ{8I)'OQTVU UrygіR0=8V+bXٹW6垓>o@kGubXV+baپ+À|(&#nN)콖XV+j0Pqo(x`zsKMq8-,|?9ल_MJk@=~CqZXh˭em5 *w[uK?[Y/Wj#ۣc\Vk ^Zϕ{~UdOeH:.*ϡG^Zxr~W)'U+!01@ 2QA"Ba3PR?IRU^}]]]]}]}]}B\P$ Ӻz]CxMHvaQGI-6w!ZӺvD^Xd&8TEtSx}w>J (Jo?jTU4 T1=J_H+v8ܞ)IʩV z/W&) 㤯WxL g ).9۝ ;7@; ).2x3sTuΝ$0@̧Š˝+?A|4*Ի.56XUsŞuƠ7'—G⵷@@j…j vOuxԩ0(PTϛ/qpo('*9`e\g>dpX;.[9~|OgoQd(PVUAP|Co1 ]|]|_+cma-ԏ8Vc.ۗi˲Wd!vZM]pM6h]+Z~+vv%v kd.WmV_> !1PQ "02@Aaq#BR`b3r4Cc?⿴V=H{F$1+Ohc?ochhk4ZAtJ >qn=WΦςѾM)7ju)c|Cn+2uGo> ~TeiFI(pqH;&XiZn\RH4D3Ξʩ:'7bw xc=ٿҁ&7U:GLݑ@φg l @fXigt+MN(WKp*X5fh'&,e+:$}sIyhZc'W'D,7 J05*$u5VrN״S22ea+ABa.uPسKueJ߭miҾ0_5`uz72U؝!{+p[ qHBvAZ'[[N2U`L?*Q$]`Řtn6t]yNv$Vc/%'1˲yѱPPRX:Vg-5(O-& QR`7bEKaXn~) BR+& rimUq{o m1ة4H1(Y#5r5" *s0)'|t*RL|zmZRdد90wќW7(k).neBQp;2uĔR?"!kMV9bL3Sb8q[jMwX9Y8f P^o)$^uBĜ}ev4f4&O0053''uA^(էg$>dsPٺVa 0#i:Omӟ̲zce-~y;5:R4eL)&Zqs.ˎy )L6_>Ȇk?a*3ZM$MiBQBNh|{hI4ѳ cr?EjH Y3W{;4zUbZVp (fS) Qٙt6&$'͒L' 4 6g hYi>f m2S@REndح3JuUEԣ96X Tq$R:S;oRsMYrO$=(0QJ94׺ qtDId;QD4rAX2~런ґlOj(&ϝ2a0lt*=Ty l6PJ-f`4Pvq`oU`bv5J@@vQ26& f0ΦFȽcޫ6pckWK3$m~_iѠ@T_L1KUQX-"Ay11ڄGBRoۙeyz"V.PM*4*{E /4&c-ĎYZѴgVpG'1;u}3Oόۗut6w$4&#gr#F+#C*ѥ$89H(4ݛ/8 -(e$1uyH⨤=QKxʔ&)/)[4V}Q'LQ&o2XhzDP,H&Xb *hYw~2B)Ǫ)uR&2 Le:>SQ"( zbpA`JyvMQ3͔bЅ(~L|r#Ehʔ$Sh}Qx?NN4Y(.1;` Q7Pcmh5:>Qˇb3L~^dGHE%߱O"+>&(7,4="(JGLe!4dc"P|/6}Ϫ>~x3qPy]h{T>Ie LR)> TcȊ$}1p+ !1AQaq0@P`?!O|| }0>b?@DI*fFsCK?|zY#k7^c>M{Š ((&^ ~?2+luy$38tdy:?~r=YϘ?F   >sbr;.seu9$NE {z sAADZd,J/Wuf=C}q"R|tn߱&QEADt2W|46iSX#n>}1{Xm4pCk䚻Ebi or3tҖ FM#YZ6^۵bY㗱;[!mZgV3in_,KE|v&Svȵ͒/(d24b&[lč51[m2E1sK*wvkiclO-T!ary u/"ϜfT&-YXԒIgm=.~]Z[!#[/gM-;E xx}8PA28| h!v;(EUWCn-\h"Zt\WY}\0Y+vy*&k^čb'] PQ#T45u_ ٦M݊|9" ̒-Uwp\ DYQPR4d.&?ϓdXO ,'s:ꫫBhб0COR瘑nibkg7mVVUB m'/.wQvfٶmkoY݌6u%aJg9'df{nv^劋%- tHaԆ4LZbXE_ʲW*HG_$y0tHhv c+HvX2+IheBaTI$]<ی!$^y7D&dz1eC#BsJ: x.݃ϋ%&ÞңSJNb'fvď~.g/ץ)d%!rm&G+O \KoN\'l}n\_JKWy˽/ wi [yeeod^JRLIb#6CTm-kQ,v1.O>cP=.&^K^8%zH_O{kHO6.ȍKWq>vIw1hs_,u~!t;~s#ЂMns% ?R_<`ǂE6K]))4[˒H4ЯkOC+c'?oۥfZq'~lp1؛>khyNE-oL?k_ff0[LN7j<kش{tֻ%&aLDŽ,5UĞ tvAklB܄0`[YeY1ZW.hhk! !+B'nmhTB3SUdwYv$bPI&ВI&|: ]7 t;SF6iV:"I4$I$К幣U"X_畮>SV`$lc4W;I$M Xv-! eXQ{XB["zAjm5=?j &PM &͌0q:&|VJ sMGV$@R[zZ`Mp&BhDw.z[ZlkZ/Aw׺:Kc/g$I$ВII4$IY ,,wRʿ?MK:@nXHӺ.I4&M $hM &ENoUJ݉! lߐ+,-X7]ܤM $M &I$I$ E`ҵT#nb_q&2[$"]`F-I$I$/w6nT444444445W>]j ywZsIb|˜ hhhhhhhhk884;2a9O5Gjhhhhhhhhk`I)lw-ѧ.ڞ xojsNg,=bS޶E f~UVvdE J _1m3j-dXjE{pꨁm۞{:MJS&P\uY?YL]# r̶4pᡡ"Oݜ{ANE%Qڸ>sf|SXX)|>hhhhi$w$Bϑ["I VQ6 w.d$x7 !adC%2D1Q)AM>EPձXZG#J!Fɬ|Jm\,G->EKƫO5$Vd%Ţp&_1s;x_l1i{jשa(;<,PȜ5JRgUTVq?a0kZkC\`? TْMK2F4@"^(̊j1O⇐mè:t@asuA/\Uz>qB͞T1X1(u *GT@u@mː]U]1SMKX.G&0 :t :"R<;Xsjwgܱ\))OՓu@ :$H3 02}I25K3B5gt :S$I$I$nϦӹֿJn:Ϛ"keDI$I$I$I$e{Pi"΄ORedɡV$I$I$I$I$wFr| 5Wlv$I$I$I&IS DxvHPI_V6)$I$I$I$DGJDZIpD2QRBO 3'I$I$I$I$4UF:f{KlI$I$DI$9+:*Hdr ;MuM5UhI$4I$4HS@0$&$I&e8{.+ǣL ꟯sćx\I$hGU3Zv35%ōI$I$I n,/p5GdXl,q̒I&&\TԄpEd!T=Ô)$I$I#s m`-02UYA$I$Hй!(搑i yV?ѿ~X^ߺ/nI4MWsE4pOYЎ(GI$$L.~{*0b^b]ޠ_c!bv8DӹQ")7*Wom{d FLiFʗy( 7w$F:~ ,9%ٴ~z)ٸd^<<̍Q|Fn~)Fn<,5a1z'lqwb'< а4Q|k~^h`N1<7 mGu%).vߥ;%|h*t8Oo5<es瘶T۷n,?9BKr=_<< y ٦u.V'z'\D{{<<@)vx[ȼ|J[ѡr=<-druaOOs *̉R}L\_jJ6' Лp*S9ATڀKy҇|z ܷzV_hPS7ydgP0vg*fwvu'!b:DJN;ts 6*w\x0+9MrT+*c+Cw<,Gc] =uݕF~,.6Fm6%gYT5!OcLbn|/5[C%\\KkmmccDSkjeZZ6ưlvnSr7F:@@Bʃmx?h+lÿ)F]YJ%DRD+ckrV]qH&RGe[>N:g]IHh߬(B3 ݱ@S$H^wW#W5L7?+mn(ڪ1J"D27&L5~Hd(moVWXqvm_Ω$H6օYF=&KHtkk%LJ&6$H"^[wHˆІʲV9ӹbd}HkI\ϤTojRe nPz|@z뙃s8 8JU~`M ᡿kp#rYU!6"AI֭>L?}mu*@/PbV.sce f;7U2o*z\+ |_;\7dJPpUy cҏ{+Y'NpӥpP 8X-Ak쒥#pkybn6Rp7=:  hloU23AW[2R pvݲB jJ  `;& e#< W#`W`e=GNe!- UXeXD k2#8)Q+Ts9GY\ԔY[FksGG(@ѝk lv 7Peu u7uřXo pm" UA4od[CaneAͼzř=/;X7[AZfR7r({tl,cg],FqJL>m;^vXšc.Sb'*)݃mr/{k++rnSiIEag]=Ćzyŕ&M klְ;e%nq?!ĕz&X%fdj>BU:`R;p*%IYYYYLDH2c=O1=OG!}19bȽYYYlb s sfO>Wшd=>Gs4'=u:/s9o`8眾|cYudz r}OGXHzEj99) !01AQ@aqP?'t}#O>O'IkjK4?7 &1&6v4g Qn:kJlNr!'j37o Kah1bρLZ?6n\yzsu"abȂoDEy@#©>ٿմNʡzL/n VXt־l?h/VC@[(؁y濗UixCGǯ:&eqY vbTyK߷(d9u|tK[5n܎ߊ!)'ΪRiyixt-oOn/O}Q7VjaQkTXƴz B6 sKD;/ _ZCMr 2k@Z.RRCMl8D~ .!ox0`𺘣B;tև=q!kFؔA`778Z* pxjTG\(M݃EU=H#0:nHm wQJJ\ ~cb7XJ2 K.oD܉+|oYc /Է5@%%rⰧ%#C 3A訏\CN,=a n˭1u2V`awJ ĸwY.\\nև.)u"i\+>r,v.\gYihdxXn7]&MJZi82KMbfτu_h/dR>ybku{AIeJ*T(u]NRo\%#w?YQ6  Qttut2RCpE(?-Wr]rV%2+%J}i߿-J]A4 [%߯V;.«UA- %zw{uݛ;wk1"gH,kQDxG|^__YAv+{-E^v6?6X?_q}N߈sr¥gU. jQc-R*&vnwvn釻 1"D6"XB8q UyDBOs/F7ݪRO_y57be&S/Of&L,nߥyLz3~poI7 P$H&v\F81䭥̤xβ(nƍo;D[=ޣ:\Qj%0r^[7:vPOޮBo7`0]ZAb(4v_׼ -^KZh'n9%ovW[zyu=:Ϙԏq7tn7j #ww1;1}L[l].'e&g= L=[? j۫zA G_iE*96'yҝHev^x";9Z˄02 /kUr ?1 r."%jBGO_ia81cCw5ilZomQտgR[; = `n{Mi( [=Ji~wXqqpuwlĠWDRgO& hrM<Μ`C!`C  ``2vO7b޾;%->`K{O,ptc} yԝ:YdSus!-˃pp>BR#RnOӴJQ= 9TmUYygv^.c_cc059}f.@黃W^@(Lx2W'pOJTIws #^8;S)`BCazeRZ)tk. %UoZWZ\AeԋQ{K2pp8\sMn{fu'Vڦr>uZTWa׼/ ]ߴ&z{e $101!0%hݍg<眳wY=dPvrE4hl?q=)``8 OP~X: cn$n)g. B?qV}GOoL@s^G |ŗ:č[|*ּ.x˄uo 9orvcWť(躝D=LpdzwcQ-Wuz\y̤10!~&Vg>1]e8)9g,圳r&=!=FӨuȤ Kɚ7nB LH5hR}ϳ>-NX9qq{X(:,NeKc#mPjd@Wiz x\.\u4b]/i[lGV\+,1p@N#6M^mYGF/~d=! ! :ziq@HYrǛ;ǜ\\yb\eI.<`tGXp7~Fݣ%YuP2N[8u_yHeŋ.Զ\OloB9\ы;;ZV{wuۼ^SԹpe0Ⱥg][?bt`T,.\r˗.\r.,.v\/%᣷_ތ+uwuzobjeB򞁒`K}'Fcˋ/ŗ.Yr˗..ZbW8>w64yh6jH=s&Xi߈BiL~5sw0}k u@6wGCIC=[_=zKRҝ+yNV v-{̻;z+PFu< ^d\03@}x+= Ѷ`N {3e}7ԋr'&tJ89/ y2hvݝݎ55e_cYYTR8>Fi`z;FN^&ƆBeA?z.KأC|uX )~4 ed=g\\ppp톕_sZgC]VBz[= [,s:=T^WUJ@YR+tmdXV..G)Vj|6-'Qt ^7//-K^!QOe*<)NVЬ @t21B\a{e{C# 3,h\EQgJ|Mv:xS< 2Ϲ~r88;::gzz-)x2!/!^S7X=w3uW#KӡQ<((D)ϲ~ 7`h]CA+ΆԆ5V?NpƾV\8G  {c@(~A)vLyO(8O(: ~;ظjo*71&Vm^`8znCڿy}N7 *j"m()QQe_ߦwKC+ v5h<ۓZ} |ёH`9C 6 Ut@ Hש珘jǔyGQ5`BA0 W3fד+CHn[T>)P`˄x\!.\rpr^`*h :Πtubja"z 'oh8 Ǹ {缮ks.LB VW`gಽ=*dHBa-Ⱥ4  Wwaܯn3@v7S|2C9zWBoʵ#IFӋ4TqC C)f=S`;ؠ6Ժ,9?"-z)CGp'gɾ]}GXHns![(yCD(* `pe˃Id=臑].ݍUz{J7 8 t㭾[.SuYr˗5.\r&L.^.\rYqץy6@њ{<hPu7ewI7pihcT`C (xJ߰}`k~=:[L^rY,.\rd˗.\/'\\|ݕ %C{<>rD@vm24WY@!j^m%d\r˗/*dr Gl_@n[%0 k(+*uE0荌7MѭOfȰ R3 nxn%$3qN *hv&.a;xu+]T"Q(f͇*n)P.\r+-r^K.]˗.\is谛PP%@:y3sKF|¦^缰XS䗕Ը?[QڿЏ_,xzsC&UVeBFq[K9m:`k̹rˊr˗.\r˚9 Z@H)+oqRZڶ JSH)SrM rNH(NӒ_dm{cAFX/ˤY,K.\rbzsoe@*2TOt 67 :߈=5ݏ8((򜓒<$yGyNH#G]%J+5]uwktj9LϷ* P"߶ٍV_qeV&՞+@GqS=S<0X rKc>ŚՖ[ EyuT Nȋj,qcUbx1S<dd5.\YxM |=،@įL߹rv Soc,˧ǸZ .G3Xllp+gUˤ`@*/n|.\[ Fk,lvH,.땓c```qqs8 Q+\@%k39ŸPal]Y^:?,˄,y؏;u._I\\Q*TB@q}7"R>dcR \;e V[.1ds\.t"FU]>ί97E_#7rDi5!^Fw /0)88tp_Ch@!+Q^; ֡Տw50\$ \&\G3&Cm*BT u#s+ W-47EqwcQY{u탃G;r^\%BQJT'fsH&~LR^kb8 \]q3]7}ݠJT Jkϗttu<~D ?ƤT)-W\pqGGx9]0s\rP[*  @*T3D@D~ae?&o%˗JG6i* W!*WTN6eB_EďԨGPt#QikJԹtrb˖XEղ(4J @* CnVGҰv 2ݕQ ?@!\0#;+AlL @P0 `@JϲVu;>r~M?PVo }[j_MO!Wۼ3 @%@@ P J:vp 4m!j'!J ѽc g/˗hxC z{N J%@ @T@//==}#Y*Y4Oպ}K9 ,vOy~SI{*[ !>?+9 uܲ:S7? lAi襘 2?Q~/`{~򄬦0W;OAS|)nF}_((^~VP]?J.?0~qQ}ܨ>DwZFyh@ B G Pw`:j9s@Tq@ s ȷSܭa?GE9@ |d@ \ACM[[^q@  rCW wѾ4GΝ4SS/Cug9r_@2@ Ƅ|w?hu%.ʸ[9rB d@Ul:d\]cB d@  o!wQA]$^_ G0dL7%ҽ*"BZO G⼔<۲8}UI^zcZgqZR7>Fk#*X>(S'Psc͝f1Xsf;X46r#9ag58!QDK$f|=LJ+ˣ^qC jq@ jvCq?wuM7"R^ M"LD爠jq&\N&36DL9eSliy1~mDqȸ֮dCwX^d7]o.f9q[ !w%9"]DKquBL y 2k5ZDbYʧtLD/$ |VȸH;_ooBIƥܓ+AuFw!j.׊ȹpC%ɦTQGu<{+!_u^On-܌F^َ˭[ xCGmA 8# 8|*"Kizo3@unIxkpnIʧ5-/u|8.{?xnQ8P@2@ @NLx$qAy T3yTzBx֍g  yp y?b-Soshy d@ 9 [5 7 Y Y­'ͨ:zNndy>$"H:x&_Dƥܓ+/8L'%h5r/*.EZxWG q@ jD:W!۾yy&hGG!=FRQGu\;rW28fja__Eݥ=wc7~Y G "^\.#vE$* ~g{={:Dƥܓ+YTu|IF;(nǣvRrO^ &$]:ߏHkC3q@ J WR4$舚$aj9㨎rw2;nk!d|^# ׎eB Z8.l/#"\-wGk?~QCSĄd@ tC'yu[+7t ࠄAZKAqTljf'N=ɸ2EPnщvшg &,Kme_uUD\ݚCq@ P 8Թ7t pCԊ%ṝb"8lgZ+"B^cbg[n@F8 z\]Wxz8]Wް}pD@2@ 5[7JvfIp{D:8rO2gqzf3aܐ2'2mg^+7{o}!& G BBE7k[6O ɚM ](X>x/oР[a>ncy㨎k ;ڕcCc'u|I`tUL4Q'v/Բ"2.ʠ8ɽùk!jq@ Hu[ٮh n:Ӛ c ?2;%D>?.I.8"ηl?h)QBڣ[$PGu1n_y67ICu̾;rW2#EܝFsD1{9(Uپ11Olwt($8P( rޗmcjn@_LӺ& 4 UQp2ѸM9o3.){JnƤ,Q$2ηA= .)ႀ7c".w-yJYp+k%ePmUEGZHqTǥR(UyqeYIt< 60%ݢ. mS>̑sAɓpY+*x3d<yx\x%p%;-ގ" -溎8RkBIƕLR A x̎`zZ7inχ*tx\-7nb2@2@ }xWqC/[(uCpU[Q6$diA϶r>\OTh캞*<3Hz E8WSe\&f^ש)Ļ3o,-`i)ȹDܛt >.N&qTk83NGqDIx'7)*.kפګu:2+♶NNz7n}J3 H/> Wd.泩|H(l+aBAĔ t?y"Hćc-"ZE 'N)7r6<\)O JR'++r>G>/p[uQGu\|'j93Ox-cـFc1Qǥݞ*e&2+e׏~+Umt5A(@2@ L5LWLI?|6%!C;9#ڒM 7#,qAsB>*2uE m*AJ$.pM礓b$4du\^ެ!ERj9M ΕqB./q".+$oC)pE-wwC:j΍Hgя:NY2kEs&\KޫirRqQ1Ryz*L5UK:@3,$X(UpݾK^C4t /!u䛻G8')|!l)uyOzNtni6)j4=U<{TU]ŶcKѐs)ț7Z1ogSRwJW|&|u-[(蜼3/,$q"㨎HH:Ou~ d#8847i{Uq^eNdUNn;[ޏ A#bAP ^M/x"%72כfAElF{=zG@\feI]rD=\ iڂP.ȸv tGQB':BnhHPm# rsu=[FN#@vPBW$=C \GdpdZ:FQUjøġ'Yķ D{T;&U<헗s~|΁q@-*O zn*8ʻc[%ڽnB閕mJ\  s2*<"z:|#݁($πBT[ɾ A#?\(\)O2wSgd9',NHqi_M]m>H:긁{qe<.6 셽x)zRUqyd__>̪ro;eв@2@ B7\]LqCWϳsz- ތ|C#Sur.戋]Vܥ58ȕa Z*ɒhTs 2!$7C9!IPB R.GSANFuLf [Ez8ZWyO5r\0X)cqEmkWrowd@ sn趶D/ʻ2*Uq+͔?|GmT)彔QԣTNd~w%&KfFsuK1|I!B_KHa lˈ8v ꠪M`y/T8-8Z88 zbvPUn33Wr厪87fhʷhF;O}y):$y1o%j9#l.9']q GXjZ~)wp]'=KO""qT>hmHzz!8 "U\'>;IWtwXWSŕx*pdžrKNL{v&H1hTp[W.omyݾ^7tNtdW*<%{ f\"䜌Aڄz 4͉P]dSBn=p疃::pAʛG%$ω:(p]רI}$Aul+!ٍhFݸZsqn'c2wDU"_ WU\!60)>(J6o&[#G sVͻz ˑ=zB..<[nF9%7M9".Gc|1tfZG^;.>_/yLD>H#ɿ΀t`A$|L$4=Vk6o,BGju8O惨y [9첓މD|r̎fxI%]lZpuK>ǠTٗi>m_ {fԸio0m+޶ݽE*تj^o*\MHx57t)[pFy6N㨻;. 4Y$2FeedRǾT>صrhIpB ?t`e-YfM|'m}x㨎+L0&S ) FfMn6*Tqn*w81EgyIT&Hl win*(Mj8urP_@yO*:BCNnxÖ,n{Ҽnf9X^z,~WSQ!}np𝡟mH?Ӏ=#|XM|c}HJ5ג%{,T bq ݥVXa6*tUTIBQ2վ;]4.GɇJ mGO/ld@ (& ^E7t H"[t;v; [oss//?F"$u2}"p|s9H/Y'xSZ /eQGhcM{hk^5C!LQTFB~-8V1G) IDAT'F%R)=z"-ke6TRRG\8;ý˓z؟GwG5m_g/I2^GSdH ȷRЎy@_M%.dPZ  7({!'!Q3넳,Z5_đ؟SB @Tmp"1LUQB,SǢ9Dr_GHkpT &o{N߉GKm!bv*wqmmZҲemsQ }'<7,Knݟݐ# K0Ij^ 7W:M* lxq?ϒpA2]ʌ-OȕLgrRS9W=$^lD|(^Ru]I.$+k:]ECun4"g;v8\L$8[Uey6uޢO A?ʒU[Vo:;!MH%Brxpe}h"UbI2JvT{\RH1 z1w7:*Z<:D@EHD(Q%CnڡLzG|:&T &t^c_$|c@5{rUଊ5EUUqz#/OGx@2@ n255,X2pCh"uљZs%<%VvMOH^Jn:\6 ~q푐Cg7ap|]J!Ha&@a-!|l>_"/:ѱȰWkpr,rfVTkW/To~#/އ¬d@ 8jZ\GƉƖnR~ |O .悋h該IwS. Wthr CB>)zy"@2!#E=L'Q $? {ImT &CAw06[q[%==#֕KZ73UUL{5vMKv -#ńЋqyK NDNpģS]i 2f &Hx{.OI8ϛ}%pH'iu(!BH7$rHPqq0[\&7>;NLGu|q :=.ţ|<'*n#÷\UqKK ??{ѥWf g0 &nAH1_I7tX:GĨqAD}n;Kd=% Brj\9C=U:iWeB tUʘ4 !?#+_-#>]M^7R:@DEgIo$|$8#c7PdpCH{`^Hr]axI @P!y,uud>ǖc6P׏LG(Y ٖq4>[Z\}TX}+r[$O@u|F8\ӽj̔pg` Ԩ@uҭ#6 Լk9!$:_@8@,8&n#&rO4yn/ ɚx9$$ [OȶKFj4o9ۊ(oU\=69A~s~aъ#*@ pPmED5HJlR:(Re[ۉ⽓UY`6i2Y-qEg*l<#!/_ǂrÎpI8sORdI9%?ŗַ%;Y2<_M TvPwCD%} aj>.b]AmJN{ї.gVi P{z!#ĂA3܅n'$S  BkKe\ѷu\DGg[ ;i4ti2f^a6VM&ɵpCua ڗb9\V!o J"mHgocy?H+/)邠:TW{9k/y`'|/g\KAw¾K+h?gWȹ`GFޟ\_z/oqLd@ *x2^)] |9c"('ᡈ.zg'xwsȹM.xn%!϶s%uއgVOVHpXs<՛GU,_w=s[?$vǭ#n﮴g!cQT>)1>*N?_j<69y]^7xCOS]LHbd܅tp?$y$n3%\&RD'礀l3Aل+ %}&G6Mj_wj#e=:\CJ"N w͈8)h'%uri {_<}+q?u<Չ3Qwݚ~X| @۔iukTqz>ncW[~~-d@ ^Dm6TpCtA}g@p}/ 6J瀏4$<=P](BsE%{׾ WBkxx(x$JIM'tI/ڔ}w| GurA'v-8 S}ˋzU=MUUivQ,gVeLU\>ɋ=_fGHb`V|)O<4}7MϤiϒ2 g{9WtdJ8F;S ^/֜rW{#X}XB,0!7NIw%Ҡ) P<|$gY'޽_%; <pu|%>^{[/LKgro_+͵U ďiV&$1CHM귍 %$!sCH8({d ޑJ̖ %KyB擐J.>$L$և\3)ǘjEIa@6}{e |rPԃF!#g 'z +W?>Oi| :NyaS&kWP>R[c<-"fl|zy?_}מ8QTkq+`/U˨V+~S[SrˍdzAPT歊Kt,=Z~G=#TbP]zld ැI ȁz]mg4>¹.慳bp\QM6qV y4i} P2I:r"(hoB\拃kzLBPėWhZ& .uO2(g.'AQ!xރ>0{V+ fW/ +S:ݽG'81-0G\5j|<tCe@ E %~I4$roDD]Crla]4jrX,_r•Zn26UPm0&U<.6u-أW]i|~tȴ8@Ljx M 7DDOTT vydd>xv:d΢:᠄lNs]W!WU#žK`l}P] 7uyt6O 9L׈$UWnUDeD07@ןҹ rw?wMr7{]r?mnHwE֋ hQ .5@"@(./ cCyZgSdNQ7\7=:=:@L"$vs5M#:exf$U`o.gkgN+e%}@eWCtDěrFk=Am.Yd޵Zj߽}t|0xFZuʈ@>V3 p7uJZv`n*g}Oc>6cdHfWGiԵ͕k^nm0.1{}arNN@e@ &5ܤrIa9 0}e;Uﭱ%-Yƕq%ԣ.[mB=I\p Szl?WX!ڔF!}hb>|~r7CsLhv2@ 5BCīE^h"ΛiG#{i`6P"X 9&g㼕pSy yD yU~#ijS::琫Ue|2砿FOAT䂿s[v4A?*M+k,V@ןdtUd#Xox'a/#i0M{TUM֨@u5njW]_BU\>I[n#4 & T&p"2}PhGtJLe .[L%{üpLԩd2y2n([Քp0*z@+rD*ۊ yQI!!{طy<BGLUN8,$m'K*q:SG$"nn{C{n5с|66noOl*^נ;f*R{۹*.patnj;;7%&"nAOs4=vBeF%$\C`6F !NO>gb3Ip6Fwel?›z*Cuo}ZQr9(\Gl]/!3^닷;z⮾tRgPӱZdǏ|=?}Z; u#͓n?>frcrVQ֕Tq]~u]˙9ܱ,|O|j7M@ *0? ݶM4$K ] Fs٤%Ʈ蔘SrΖ)~37vP;V[[ 8>4.jb㪸ݫ.>}خ^ıX)!?zQf-wUT8|4gtCI;jMsɥd 7кhDvՌ+'u"O@)bԸ́DLc2-gVҢFco:*Q6%\C7K$qC ]mYtzNd2HpP9%WJ 7ފ1tBvWQF!׍.vLeeXk}er*ĔV+K|:䠎7VJ\ #WǿOG_GS͆S~ y/+[aI/4&bxqZf*_i>ceM@ Jg-$!$sr4.<0Si˓% 燒e2e#\M&LmdsHFBG!2RB`V9>r;n [kgHiuEue}}UI%E]iƇ?HF uR(Yځ k%ȩ{&Ȩ5W(1|V*w~\Oyl{LG&s4jLnU5RHwl(pܦ\Ν>k6p)XA=|A$0w1Tu _:I;qΝ# 6Tno6'$)%8#g.Q7fB"J W@@֚U/G9~Jܧ[9B΂%˜522Vrg$XQ$a@Uu|k{p4WyysWC^{UTeϛmLZTq[]Ro*Jӂ~ϱ@2@ P[\+4^6/|Ч$z' Vx%]ʋ!0wH_{\5/ȐwsQO=!un$!p+pT~93 c_1U9O \k|:4# D\%:5\ip! gkӵO*I~: &)@l .urmz`'ނևor@裆B.ĭDp 4w\M`Q7܍_ 8)>wٓiQ-O'rVk謊KERmekok8xo.kq(@ E\#~?) st #NPJ|!>HI& o2 ad"ڇ}3!w}x!"3"}xa.B^W@a03u kcu+,tdkIoͩ?w-~f#;`R*4]ګOUg{ߗp%%UB˜Tq=qU^:~6yg7',/q ipLIz=JM]UR} vYgBs3cY}1֌tyf2G;,X[BWIT]=]5͵Cd17'NJ}sg+o*~poC-Tq)h 6˾t05)k4HF1o G $"^D$\I"i]pI%nm$D _\^#88;:%v-$PAЕٺH{H3X2.ҟ)vSȍ/> v\ll]Ρ|8$1_src8(U}-UǙJבֱc >Ơ$flTY*L}^1]fE]T95,%s\CL%`/w6kWu!}ͯGppρtUnc|!4]2Siv)UsJv~m/Н38+t(Pűz|Œ#D\UC[I>;HQ҅K:- bp%)%} G /X2\G%rӯQsxt*JA/~]:'Yjv\#}@[Ņ )GBmy2_Y#Mek0 yW=>uk1(QРdA-k u##F 0ox~<n^Aӽ4l׶J3\ًc0즫zߘ:>1jPִ|i4I @ ID| ib8S7-/˓2h˖(|ǜve~ܱc)"F /5} Yն `#Z[{>lh~tcsь>:nUs_g/ߢKqc1OhpM}~sn9{{o0w^0 $ubҤעE:lUTzʧ׼Q<Ө,gVbLUUqVweL\vk[)TāWU43(({ ސ]OSKi>7,;\e\Bn*3zc r(:j>gQt_*̴~~{=](k:Waj8O~J c@(mz\AUO9Kv\L}U{-֔elZneb=f̬}r.722%d@h8qj~ۈhcQϑj |pFxH|^8(T٠l#j xۯm=pr1*nڷsPSϡ.B.D ߦ򾉐Q!_}8rJYtm(O[Wc/ JP-ljq^aLK墊\Mǚγ *.ƛr>MF88ptM7p~r6AYZ႐Ξʆ8GUB^E Wce yw{„q>\ sYrJK@_E "!ו,H[ތ&,ovH",ۼWq뮻oWνjQY@愢3džOFm?>fҪw]{*.yfi:cAM6˾t Go|;T(17nH W牋/=}`dâ l[2mgsY7^>FG3Hw,}[j \9m1Nt93 X Ɣ'e_:{;j;88Pq_5D[>шt^8K ޗAbJm+qI&@ȋlv:w`Afc]|n&[حJ}LZNg:(x0啹7n_whw_5dA8Ud4.h변rM%]#Q'WDT@ǙlcTvrw/[$뢦*MLy$X6P%t/:NA%%˕ F[jj5PdP&ඇ*SfS|%uf5YpoR2焐Y7!/_;oj%~4y]}j^W."Q4aWRg9QSo|ݺgUWwQmSWB A-ebL-g2LG@;{Z9[)@"#=B􄩎lpp5]A\d#cuK&Aoyeʞw;߾ >iV *P!4,}ZB3'CȁG~ٕ.W_K:W\_JG`o3g?2ݽdkN Roԓ>U嶻e۫u|ЦJd\l/;4.zZvL:VqD]=*ppQO xoޡ @dty{MGӣ8hre;;crM`l₈ ~-UF'Q3Tԃ@wžɘcG2+=?!܇Ůw~}{Id4?!/|qr̈́kth4d^4C`C؇4{0%[^=|y&Ȏx0y锨 Q-+vL6> 4u5n|TXJĉAl^B|(EoNe(qRÔETľ'׹z6֘re{,@[t{g8 Najf^$PooWȫ*YutQ]!הWFEluPsk5-uŤ}}UsushjҺR{:IO'y[t$zqI Lp; BE+q!4% y %2k7:ya9r !/_+!rnă5>g nky%Aq~es;;zZ"V:&56ۏ,; "A'Lrfs˔Uq]]w2&Ky# ' G98ؗDe^.j(?ipXpJ$|%= .hs@UB.~ˑÔ/)S^j>妇@Iz[\#NYTTg琹&Lƶ!!/*7B4[l2P!z:MG(?-l7ڗtc=0\ QdI#1Kٷu(+ADt9=n1n0UUq-,Y5\U\4yѯMe- WjHu."}3IƘY6pIe5Id̺ᔄún XeP™~Kؿ_=.6bC6t޴]ٞK>  SBKߙ h)9k*msȍ۷9jڏ0 8-_9  z^x<ۏ n0Swb%RC H~j;gUsPu̗A?D Deth.)$Q'NA b wC,՟gioX ;sqo٦ZKqݏi9zxC>A*w/}W7f_^, WSGn8[vq/qUI%IIYn; Ęx ?.!= z(YKݝEyBTN,?~>n^*( ї8FmY{x^*%vl'P^ *L߾`PDž.qMuF!++׾>1  LjzhqE%甤ǭ6J ֲFmr7ZYTf+7h<Ŧ4htn1жOuqH8g@,< DuH/WɄﰹng$(!ASppKS툇}CY@hrUP2eޙzM=e֪VSwQ/{>\u=?gmm~.Z \bK~Zk;B0gasmeS,]җ=|u?..RPR&Z -@oUT:uߜ2K2V1Z"+c .cʔ;@;{@ D\%ELzpnfvn8'ۙupX 憃JNBn3R Ί\̺`>8 g}g}}x} ƥ"eگq )r /$K.'+FW]C:4-yu]}>{n+EYi %(cXkecy i]Ƥ*ͮc̴ Nl.}G@n @,,dBK_OOts+놧ـ5Õmy=K[3"}U 2.v:M> Md3؟ [zrz>gN۬ǃD(Ҙp6~l]0̺:u=OS.LKw^6bx\ߐv‹[z&9ߑHĂB!6UD!,M?qnop 5D H= !W߮W&䱑N QbR<> VQ?z>v;FA3ZɦDA&D\w+M}&A?%"G!K@[Du{g>sǃ'/xp¢EFXa ?w[X:ݏ5‮\8/Ko2s@ċai#pnx ]~jcҌ˧FucVn=[OH8$bAuj8l;4&M/5|+QS-7|L"CDqz\4(\ժ7/&ަrwB.WUFK.VȮ:Z :_$\&3[ ?)edYw2P=.3f|T؇Lǖw,%}x*}:')ut(unkd{R PԇlK_/5}7jc}cCg_nr2)l=q6_=R󵹒8yQ$[ K^{Ip'FcvB9"o1̅5*Т/Y MͣAYUWYy>!]guq15@M6w h0H\ѹS{''-Sġ ҇n|i7%IlJ`gUo9y۵Щ¾*Nmem|&(:{~/' ׍GYW=_wBmzlSqїOB!"WuI$2.s=-?8D}U7y4]lt>2Wso˽i 4)^4&cY 4Ho|O@TwKWn4j\b2\ g8=FUUc6hұTkbwc`kU.*JpWsvN!ٴ}Ol/.l}T my~WMq0iwi_z9\u-Mא4]0Һr:B g?|MYqa|Hꠠc^(2?g][S ׍ISno庱~g|Ozrinb q\7inHi:α,H۹}k#ゞ,cv3 vn8=ꄼ~itk+)ZTA6;UrC52]3rzTv+óW ;2Vyp͘f}nlMkPǹvpn~kOpo`Bw?3@@Uݕ@%A#zO*Aש@I*&Ğ覎@ "A'Jӟ:89u[ݜ&&ҙ+Љ}i1]GSex}X.׾!~\b%yxlI}Ul? l+۴\s@ۍsL/8ʹ{co>1ط7ղNR$c"d<'k^YqPD!߈} K}=h hė[?B _"fEwYgP_xE]}H}k[$bn` Uڈ4,+qqF̽ <.rN!"d.레 ܻP4Ij#F!䆱)!"2@V$d#P3CF΄\$vl؇ AټpF%B擗yp04Wzǧ_Iw`;' л MDK)9#t[&U.$d~ª@Dg1##@MD4jSpX|3q~SN G>]ߊ*96¤iKUv}Jm.ci8:'ZuF߿;C5Jt_#}q>6Wt!׾\}kIc7u f~ݕ|_YtY1i"m)+YJ j 0Z k+O_֬W.=8H!!.r[tMLZ^ @a7l1\'eS.,+5qQ#%!Eʩr[Ty%%aCރ06ol[x}b'D]wDsͥ>}q^[+S<ΧiYP\W-7g=ìxf<\'9&)x~2u| Fr)$覎@ f EI7k{Mo&5q憃[ɇ-ă O-Kq"dCY lZS`ҍAreٱgby!L-9oi^!WUDMZK dN9*LۇʶɅUeHf᫯8tX ewgRD\g$:pc\봃4]H4]B!<ԭI[[|% o&k|zK3$bfq!)"zSMA6Z,@#|Oy6f˵(I]IAŶ/ 7u+ )l{&DCmK5ӵ$!'gt _,eKI>WRk#)C]wfyԇ. } ߃ r#c7?4wwOoDC *N ce5J-m2uJ/(Q?By5ԳIr͏WԮwNEJ j%h-]i?}SO6Yt+>&Ѡ_܉'!7hۥ܉5Xd$^ L}m=ط!9]"Ry)sl砻.(DSb:ށU]m5gyj [o~4t<=BIcG7ug\WZ^a?!DQ֕Dm܈Ș;<(YВ%ZIz[ތ@C#Fwq1uHDܶ-]$īfvM ^8%ݺ mCzL&s)]a[4| }Cw-kgy奁ڈP~hL-1܌}רbF+C a_ ojpqRb>y8C{o;2WLGUڧh#ח~ϾFz=1! uqW/o"QM*KmmƓ=a0 7<=JcxN} q1Uh5ݤDCoc$w'ҷli6 Rt( #JGm{=yKWjZ^nHy]z})Y'!@ v&䖱fSy!'v:#!g[`>΅к0P>N\'5_SonY+ Hs/hE #[JY0"|[Q3@ z3wUﭤ"AڀT9Ŝp Cdn8A(q8;7'IM>F\D˾eCsFo:.R/-}[[?EDZGfuM0Ĕ|+e&n s] m Gȑo~yY1_b93_D| @ĕk_$.Mcq\۩(&G8> GQ|0/ h:Bˠ~ 3 µKlm#qp 4=@Hjf&H[qP4` $/><{) mezґp.* &#>q!esp5܇Hz]\Go\Kr{/=An{#_@Jc"v"Ζ.s ?d};>A%h Ԃ|}|SMl7t.]Œc]S۵0ֹ^-sqY7VIh9dj9?/!gk2}>2#1 0h7|g/uW^?oWMyh9 . ک v;^L?aWbʷ#A"j6"~Iuˆ4jF!Da8 \i$i}bAYB^3 wE3P|j&K`"JUe l/k+Z \cLb:ʖ<ЌWo Hb`V0n nf1f1={fzϸmϙC9>v vi``v%v0 [꽪jU7'"3#oddyoުw3c[y?<,%ȭR]8n7? ~uKGpw(c8AZv@ܚ&*3aQDiE KǂUx?k7|g O>MսËa<\ċvgT/m#2ku3-<צN2¤A¦)R* ٵՁ"TM!WܥËڮ: >i595D%Jp@&rA/@u\| 簾,Uym5E*$$P wAnVf TT li: [9`a 2Zc{3n ]GҀl~@^3%x໪C:z׼_yxsS{DWB>q="C# 2CQD#bbmz"~n#'<~m:C;Ȇ7$Hr"p|M\/ Hy H[@IorL* {[m$knMfhs],,-.#B?v?gaCˬ7@:Ag}w#ͮaZ``N-SM'1w͋^rw>;Dqot8_AܮC kvx.0ïL9QD '*ۼC,`e{vEogW3X_[)!|(RP%.ݽp 5O8TNR啺jLkJInX٠n[JrWTϫ lr9b,6뭥~+  5y:^^{]=qu1|ǽo~tLt kaS L@'a͐-x(Qˆ&ƀ?7U1,]/&VE],Ζ.΋,^}O,Ҧv( SMTkʫAim- W˜OֲBw} ?cP=L~G= CL(Q4 cKy%?x1,=pPr~măPu ^ő ѳ6;-ۭ6wUavGPd藹2p[ I:0S9ո~Pl'T:U>qMAX[mH/x଀D^O)Csk󽕅C}Qb7+tVg6dN%XDxvlzf3 GwL7N#S]A\]#!*%J3q#Δ7!@׋[',v*< TamD؈$w01y]6l_JSOjnXb[GxY7bl cgP;A\$3 t]=P8,.鞟gC(;f.8Of<,W8}th6߻  `Gemwy=A;bX3­]%q=S@xCлz|%JoQ@4Oq)׼a[TWaoWog򠇱(S)]qaiyTG4ƣD%WE Ox}k"lx9O\[M5xU6u ww3/eQB: VS Ů* dS7(J(eqCy~~;ٶebXzZz*zrt Pyvz| ='?X>^ ?j&}.24鱇lH?(6!_ 6*JDR)bTS91 #ŗه\v5{UlN|^p6{Cdi`y՞r6nvX{%a1cfOvsO)+:|K SU楈^GeݣEx(QPĥ,x>~m^^xeh:ɰJ02x?Pn L9Zt@Mj ,=E TϩwNfI 6aRϔ3^=~do(sچ9R'r^6<]S-Ph|ur^ӃoOϽ|tU|-mz= n+ `/Є8aT}&3>,'|wȹ,:Tu_ӵ:e"A^ƒEN(Q#qf~?Ϗ+HŶeٮ.ph6Vn}QAháD{i@!'M^r6>3dN~KCECqEPEx7\xyfPqfrSr*'e^7_rU3XqNa"dX(QTDq}67\|bm;lwc#_Mx%|Cэp1,msX萮Ék;uzt y6& l.0 Ă /, 1'S\CGsƛH;`Lyy:V!=K9:\|ͣ^_o>,JkAV& ^ӆv 文@\H ƣDR`tӵ?ΏChb~?|l ƕsX[mjXMU1l@tT\!Zz/*7ArU% )FirZycIR3.W76|y ފ~7/^~O~;Z_\⵰@ )q{Qz4yaYD0]Xx0%mq aB#?]P xSk+ËM wvr7l>-f-Gq5!-5>WNB9g9. LjKr:vsǁ\wQg,Ƴ!${[~/̻oӳNEAI_ 6q+qp -4m IU#x(Q .Eq)/?%֮?| j[)VJ?}ێ]z,t5 ͰDj= Ple  !\Wܭjc@$dO #Cȥ-`lg9O9_Cήmȹ^1zkzhS/;?ZȢm A2 {1z A\oXMAm=|JGscmBm6a髧+o:~2s/6 d$=  mD#=ș #HwUrSx!+>'`-ͤW77hk?!PYڅoK]}K/yW>+YN.8z`X)[_&`T s/4TN롆&X(Q.ޘ?4/Զ6 }Pb1P_WO~ MIDØTHrw8( ~u.46*xs\V<^nͽ39Vq瞞lՕ"]4ggÅ_ԋ?~޽3YL@2wu8=z=€1Lӫy(Q4q)3~<eSY?0l3u~;@\aure<ı] c- 0uu1 L <y> ހy:ʥC7d>@.o3h75mO9W1 9!=3g)3 ޓ~߸g|?eK8(Km{yPH߿/~:E%:2NB 2/$x(A .K^~\)" w1]l~jbwá" Z H{,x4 ܌@h`bNȡp_ %DQ @rJ>_xťg<{>K:| eJp O{{7vۋxq0> >rkdDhQ#W9Jy1Dh`"0rSr#'wK:4rH6r+kr}~]M۟,'gmȋ}}d?ޔ9; P^ Gqyo8rGxy# n/@=cDe'@(قkl6^ %gssxPFBrD]s/h1a%W@ˉ@ 귔.l6\ ! Nbͬ+g[s%W{#0C΁1wZE uZLyCˇ^7nq> {CQP; 5j;qs˷2ln80ƣDoRBmx7?.B̶ + w?7Zͳ =P$ac߄GL0K-r@<H]M2 ȥ4Kl S5m ȋPC֭y$b.xZ/.\"n\c8oೳWm\ρ?>{v )tx5S(ɀ=6|0%>˞ğ0[ ϼ\|tLܖd68ݳ2?1CRYv 8AirU:6jL? o!380.@|nnrA7a+pr^+vjcr^$VNp\-_Sλ~w*ﲲ/䠀84a24$M/ƣD'U//3~)?Ċ鼃U_n]V ˅x٩uoLՉP7bO&owsz᭫c@aC@.ȡ +VE5E´\rsL v/!犞1·x5Oz˞y5?Ek_Sxȕ`AD|@ zX!8^@\\ X\FeH )?~-LՅڊ!K^.Wj3v-kJۨ xvȱ nҏnek+F(^pO!c`f\7B3w =*,I@!s\{3.0s?etQ>HC=vOg|K}t ; nA!{ƣDbQjSA\o D1;~f * !b2b|6ၜ &v7ѐf LM^4p~uj8cD(ɮ',R!o+k~>:,q+ ku詆B}ްLZDe.(W?iOY1],Җ?uҸbz<^+:$F[JVp~OrW9tFK@nszYo륂G_d0-g- .WQ/ Ƈl>rnݵ!8%PK.|OCbj9CgX[q0/EB8/EF$zƣD6 BX÷sj[xXm>4=u4R@N6a46 >F &u1Gqv+4JzUjyd rx>W|!+gqT9";9Zoכd'Gvw^:" 4M1A7ԁZ{QqA<QL( npN{<[1hJ*+7[ȑfיb:NNCs x۔P6ȩ6 &X6%3ȱQPlO1@I ϸgCy#quu!qȹ>{cC7^'^&,m3lz|p!oal.x(S H"??Wl]#@\Ym4<][1]r>Cn# BA x1Z1cpbˆv\an9%ܑ@Os4r1.og@^Ӻ۹{>r_C]{t֗d .f ;@ nY2;aK_|;8`+wb;9 -_xywnW1Jq{ #W/U x(]qZMx˞jxeS'FgC!Y⹚ nyp~_7B)9*C0 hm ǂ FT]j)@nr1_\li&+ 8m}G S9;9Ǖ G~I{Wo;3)z=€X>x lc#GQqf8u.{byP[VW\΁\%wb9 Q `A\M6|ɨS   x(|@r\XX,`N\-a;viy=rrggOz{vv4 {CQP; ERA<+_:axtjks6j&6CH7|AQ[lPuD Ger/5Q 䦗P8daN<[\ E/n[T<E z)@[Gίg9lez1_(C9/6nj ]D@9D= 1_|]834|7zF)q3}cw#{2O0a]$AVV|n$i\M=JIypn?d̏=@\,Զ{m4(Yerq:j>+t=e@M` i(xMbAi.96_诽QtS wAi_:@3K`3Ϩ Trd*I~cczyW<)nK$s@#A|.{?伖&ۼs2tM #S3/NO(Q ~?m%[_5]lq&:{{;zJgIZMC'ˏ,U>7JC o !ܤ 0k;[o[ ݃PCm$Q/lb񶅅|qqsJ?C΅sSZZ;^+򃕳/! !m%a;1, AZh@RN~)a{,"G2F ?~eO, CG\e[ &uZܨ<|(w❗e Fuٞ4c@-L- L?]r{,~U=f y2;[{%Ӈ/=`pC+J8nЉˮs;oęF6k:pzVMߎVAp/"G2&A8S ]~eOlC&g . S'F[ġ3{ık/I);NM hS ir[A[ĝL_4ZW6\ Q+g0^xrZͿ+e)qoԅ+-ayn]+qp-,xqA< ܋-Fe Rp=v~e/lqGԉJX ;$2ț8TVoxME?YEچP`iN!W6mEjAPGLmbzYW4K<拟F #{ 9wnsZ9rN=CAցӣPA^$qk(Q 03|dz]T ^]ɽr(P\9-&]@ '*h G%~.N}3zɻEԏ]  ̣x\SqwXE]C9 x<]VZ#Y@W8uS-R.MJ 'NJ'3St ѷ _]a|`BNr1 2l~+!|8|6_o !u۵8$|!5z%UGxb7^ƿe<Ӳ@SpX3CH7|[ 0qUj(QPWx˦< mx0.Y͆ǽ꧝;%E<)^gU*U|M@\MX/<"[3ubψ6 f]/6=ca9Qelllls*mH[ 76b̺YCimoU%s?{hﮝ$OuM[ ^ 8h L@GOv@y/X(Q M eswc=qu.VPhX9]5.҇q5 ?6qf 7R`uS&/14)~]2Lmp-T oOa;z*fgt(;ʅۈCj'ros}$|C>C_sxg i#tM^/dk@mE%x\ IC/?G\ MWN('y=rj "׭'$ffYonxhKφgieao  crnj6$e=ҋ]kxPV)A _+ƣDi( S71֏`ǁZqc.G|f@ܖ1tM_N8آ@"W0y os:aR!ݟ^[r/]Q. Ul#*m6N=<>ʬ{gC'E6q93 lȹG+}Z|_>a nġ[3m L Z2% B n~8}j;z:4'Qq5_Or@N) .V4R#}Λh _*ȅǜ2DqCt2#!!A$2PF@ }G#4 uu]V)2fâfG>a9I?W[e+[^luoc{gNǐZ>!= MK5_ϜAz[0`zYxxճկJ c:" ސݶ$y;ȑEjJVzrhr=aPm 6SX!=OG"mu.rzq|9ƆC]z}qzsթ$A*vqv%|J@\SƣD:3VK_.Ý#!^ WRtXV^^Fj)Tp !q/$y;C,-rbPIHXfNm9Y7[\ Q0~hqI}w3CkqHC1:j{蘻bi g7}w}Uq8Wo씀x'qz(&! %x7?9.戯]¸^.v: ^:߭ݨKc?G1z\fBܸ!\rNrFFS:6ۄI@L<ȓLY+^ Q'ϰtoYs~=UGfg~1z_Ix5S@|S m5ko̐a0+==M@,_@Gq9C^.ܖ-XQ>!bsM 9;9g#oWp /waa@S [rAr-~7SƣD)qbm&~\Yq}h`r*[5] M@\ ȩ 1%k Nmr}F` oj@nT㡟Ym4V`qy  9Eֿ0!f-wrh .@N 9r) Q~1D]xUs_>\ SϽb;@{gW>9;9 Vй?KFIquRI3;ƣh8X.{Lۯ$?umw $U>. )OA SD~ם/ Zip]&Kɚ"8z1} 00Hv D-8Zŕ-e!)6伖CL]cK+wm0}Kڴ> XA=ŀ8 @hx,-㙗-x+yվV-h5X>}j4G\ݶ, ĥ(Ol'Ԙacx`I H?붥 >@XK^6uW\INQcr=Y5zq]"ەt^z6D}1C _b slmi6NCmz: ޵NC={ p@[(* k/rlz,ZV%dAU/\@ 5赥! 7?;-ۘ )"G908ftƅ&?ބ0vsg` Ι#Q/L`Ab=PgNtcySo!~rL}DwBvFl{y }^*d żpP+.r;3B>X' zg!uvw-ӿ(0zO{8D-W"xqfLNQ @\rp7;xhbmp gsj@0mj^Dfpä73إiz 2vMW,y5Y&(}rMb/<\Wntqȹ HArʉN/aW^tozgX6-lq{ u(C #^}ϏD++gs7kCӱSi5!o8zlK%^6:*Wv{8#²-ͱyC\˽ʽ~?.vZrXG-68ܜ69ۃwݷ9 RA] G jl){@|FEq̪6?ď?Ǐ `'roНoav/RwNQ <`Bxe]& 멦xÅ;;&=Ƽe o4(M\ f} ]J8,{Tå lT@Sng.&zp~oXk{{wrf[Kf^KnscJ~yuqSZT3v0eߊ'0.8?OAVkc@T]10|fĥuh G6+ze^@.!9trB~) ۶9FT b+.Y7< O߾r^ @prV~kK3>]pv=>ۥ+W͵o M(;n Qf=a<ʾı{חx,Ʈ[lڐ ^։UnJ dT)m )/7I^D•GR!h%Z(i-UV=I@eGA"y,S /eWA|pu淿Ŷ_p8伹8ܜ08|K}±ؾiswӐ q=eq`A<0eI*lñį0ݡlguCw]:LՍyp2njgm(OTm7qD*۰E I^/2pdpޔ9TTX.k^ox3~hr9D2qwĕ4I=]]fx9NftQLs Od9) }RYS\ IDATnOOg?;ЅLdI4-etN)M9ʠ@>N{93gA%l9C/!m@ο"336F=+.ψU+C牮vvm~뎼zMoήwrLGg{/}sc~]QAQ7u n clSiD x?( Qf97p0Hbw7qSqBAu:& -.ד0Iz*+$i G&x^KpTg X&bRȪά O/3+o ޟtW^cW*^őfyGVE>QEuP՟ן^l%@MRIHpYf5,)f< G.KDh^ n*;h+SW}˔0CFH- LxvH%J67yğϏ?jrj1<#r~rPN`WMpJқe~@\O k⾺)mk\&uCjPe$t[fqLpClPuI0qESrF$ n#P 닅2?C#\\˹=e-[E}O+!x]zx5ӿ(PCV}%E|ᅋ}~/Wl7+X/ca6Ҵ? EX3eEY5]=[ y?@\#~RY9]Y1}aAOqV4QyA2^j>lbuMC>IO8$4=FrpD/~|DЅEm#h_|xΏ#p`4[WX7{ ;@:U!ۤZ*fX#/[-gMdpjkqY|ba=rFͪxl ^o+\5SYlŒ^e iH]ٔ2bqR_v% .@|[۶L¹KOHymz+eR=-Z7!ؼ= !6>p?iy\ 56YK**+$i G&H=NRY ȃMm~?Lk.ܶU_!txU]b1mŷ axH=i5WInC6#n c*`BzRs*z85"8"F){PGx>е.z .^=gi e 1SXI遇MHZ^7`ʰY7=z7A@ڶ~/= .hRfw&6 ȍ]US ^W\g uuZG8ic7^Fuwz\9 NlD|ok2Owj-˂OIR'./$$EM'i= y]5JUĵڂgdu0QNCm^q?Fc;gV9lma&>աbhj}Xୢk [(-+J/S+ ZҚP>ۀ| \Ͼos=ғ*+$k K&H=!79A1e# '6,$LCU3΁*{gٙ!Ɯ>䜮* 3=vݤG@ae_9qX[&̰t ] =,}|hlq.-lP~5. OLּʏ$Iu"p.iH>r. SAI2ԆJ8׌Sۙ-@|Y]A]3z'pk˙^o<8Wy+mlDӻy HA:7cͽb{2kA\+'2AX[18A+ du3ej#&\ˏ ? +'.戯f;g<5@AI}C^ҼYS ![G#\^0߃䭵MAOv{Od=ñZ0l#uxخ^lrl=30֣^"ޞmb / ~X=Yk"dRҿrn5Gz~ks~}qVS& `ڞ"y2M#.&нQ@|8d9m~Ŗeٜ-xM㚔ْN5 .O)L BGCu 5|>V& 鱇"}mmcߧl# hb_q3\q~lg&<3^w\.9og7T tMrviȹah/ >Z} LEOXA_SmeX!ġH{uJkM!ɖzf,c0e*oUx.&= OU,*O3l 퓔 !TOI^*q)"C0C% B# Bsj[q&$~. nAa:c,p(6oFr͍@W<7g3 M j٬ w{79vn:E{Y\FԤIrG:K>p]aJ(7H2ZP0٫^k mUGaG&!WE^_Ff?lavROd1]?~MH\{B3]FڥچUmM5rWe5tlzR<r0qlF5%ϼ |q?r,aCʅ|3-7pDn{YTi\ElD{W# m׃+^U.VsKph.%czi%\6A\Yתncl =KLxg#G2@q?C[ͻą;-V/qާjĤXLI]lPx h3߂P6PzpSkOI6EL-, =pD3e߉#=٦o+>spe&^q65hg9Ni-G%/[5 93|&my4řW?|~<䌞ŤfQO1d &[|vCF{yT 51g|:.bΘ?s-?Ï 3w⫧۶y0u<>7clSi7ơ >}b;x]צ.=6Bs$] k KHOjxbilDɼvfrxbK3 ԓYrEo .h/=X͞{=V8E1a U#)_;EIda$|ёvxC-ܹ^ 2N €x}gvflC^Gx 0<a!#Nl61|K[m/v:.Qz%1VT R&OPoSz5$]#n%W{X9[(Spߖ\ _!Q,C֭\^#м|kqHtutz(d=@L6X6z:P抃:XmFx%!ő#ߝ;!v!Kdݝ G> 4?vnSA^/POשUIvVEf:%ԣ1y#G8VP7ŏ$ > aܾL+^ S/Aq! _Lp)mY`ZVv.BaD Ht%p=%  }8^6}KO"\q(\-psœssw݁L=8ܜs:S1!ۤsn|CflbeS6=9M=VV-|`=)F ZH3E'հQ(6T»y ήۗ!>ogA}pjbʃCa@\=m@NlY)t<g:#An%dI:!im=pD QR-1W?gf3b;3 \ }R*7_ ݪyԜX[&]e|hWT`<2"\ǏWQ3ѐ[yzYB< ~SRKCq]?$yZO{/Cyx} 5s+$]C.mUC)K³9`zQv bX_,6sh6O|fYniQo2W\ȩRj9JUB#y/Jj8& 1ߛP0{Yp@%RB >BO/]Cj5?{{޳xq ^<: n=Ys<^4.bz!yxl%3^7TPxgB=UVO}fOGWqnABf N!i-m("gæzŧ+ΏdƿֿC\߾lH5YsnD-6ɓ- /d[|L?IO  4`"'aQ.!b1_)jI)g ~*L{Ͱv@ܔ֮ġ$do{3IP`d,g^PuqjmF[@zLXcaPñ#rL?\qC˹GԗYo~QGpwϝTT?/#|z.隩* _0h/ˈCέz|ސsclt,gl&aa9dJT *5L&[ֺ`=QynQV.4{;ۙ;~'\zp}9Մ$&WU5MA\OJFٓ=>ԧA@^|W]k d积mLܦs{K+OMBN}u~BRC?0VIjw5Hzp박H6ŧbm^^jNs(5Hc(?hִ2c(㕹0xa*ǧq 扏3x7[ZD0q5TL)ߡ2&CC:#~5i!9@N7#<r+.϶6 nm ۽޸oUvi7BAdaao[[5=1s3-52nOM4iWִ2c(( {Z( 0paOxRY[z%[e3kٜr64malʧGL4gD-4DqEimA?<"8ӆ LJ-&%7$Cޚ E!-z`9Zgl䩨aC6Wq !b躘+>;K(y]V> 3FK }?=JaSg?`R`v[y6u)iEC{<`D?JO2R!PB|@z7DP6PDb2 rhz s+|\F ,Ys!2Y!  mm ?ۚђ},Q?{OYb>I>'U/LҤz*i7$G0d=&'D25Zo_蓹K\Jru9 /QL" b:b2qs #j9 !iMO?g00]ϙHWRsY+xp[ gnζ5px h-z$ ?xʥo5Nc[}&y v3ѢF^z̏ͣFDt3*[ڐhxXQQOqqx.ƏΝ*{ W .>9yټrJؤ;|J`MAcc,U6*M4>*$%d{9v=~ѩ"/7T0MCDQ^ +.dtK,(vC.+~zR 1I-:#mMmcrj{|U'3a~}| n*cA0(xN#=fkbNx9<]9b7j7U>NN]TQ/xq6YARG| 5]+<~y.y"0q+9Ƣ_[s!r6#Ԛs=5?.^H u5o W3OldM8=Y BD ~='6#0{]zfKAv~MpŢ2uCB0J +!!-S.8Fo `0qr8/:WW|i)_=~=sMvYO SH5Ys &n=!$O:1$ԃ̈ql{O޹3x_YKwp@5YޒYnǭ)spSU:n_v2/AC@Ԡ0xߡ! HjA BKHF]n~(]d oF XǼ&zQKBٹѾ7HP^ӟX^T;@&\!uɪ<8h/ˈ4|j_[ķ>b^"1֞&ZT=ϭlc}c7Ϟ'ɗ+ :LE^k%4dQRW ڻl3΍I#Y+Y6zEC&ZFt_ψ 2i [(QbAk X~IkanTEHF]+$= exM*֏KŴԙvfsW3d&l­cgUtKH4 &{4FȔfHL(:q4OZMFjҩ ym*l9Q&)_?sMu wvr_] M/1+„G<Œ'nk|&"Z{S 4-@맂3Tު1o0҅9!u9 U!rey>yr/g^ Yǘi If'˂l]8WvA&<@ =j6y^wo&+/ӌ[ja<ʤkw b UzX9}g;Lj`75Rem⺪W.@-_sHS&r-T!(v!Q\ Oj4 佹#.s?:lFro>0j:e)է=%IJ|ۘ^E{_mgAXH>V$KUF8L=J!Ϗe@\]9]/W+ooU ?2hAT؂B|Q^ nIƓR.Q7eè'YHmmi4%,$:x72+g}+>S.= V>|HQLkWآ,H5Y!  mm ?ۚђ}sKێ]^ԧ-&-&=!@|7e0ߴ?e'MigzhMFԤa1p}q^s>6ql?ZJZder<)3z Xd^2[նA1\~dut~yٶ}H9\ ֚7&=XȠ5|g6ɫE3Fyo,}bR6z"M!׽6G+Z3XO>MX!˶)+2Sm;<[+.sv):<؞˺ݷ-͢bE," xb&B1#LxbG_HHb,XF z{u*fevrv[u2+nU}k(q]FM!犌E[-swXɋ_5^9n=;yl̓|3LyT;[,ʑnj 3x ת)̄ӭ3?c0#{Cy5 ΗbvL[(!eH$yC!eKsTv}n[ Mz fu6[/^" W@8~/r~Y(smlE΍k[\{bxx]l2hjֵ;.N~<}m3f{}&xR< ~O?4X~ K/C!4^8~zp[:rwE`6^q .nq=hPm?dR;CP}@ي!x1v,_vP$ԫ}'T} ^2 nc?|BW2B?T]NbR2vRzC!e;Xp%rcۗ}TS{&xi:|.|+z>DRȹ*vu )T]rޮ%D9Q7=؃f}+'ON=:{Ͽ#ٱE'Pg0{99-~p!Tzǯ_aGN˱̓)24⼍ U?\'oX^}pZΚ6bYN`939V|@@rWR'dղVUt*36zz=kzx'~VdQNJ >c62 66E/9Ԗ.=xg0O*'9+~%1::@Q~2$ Vض9VӶ[rn[Y|P@B& 7I@X\\B .B9NX̯IVNeyř7֬-׳f,YWCf;i \hiwm|6L~!< /m5 'FO ah~r\n4?j7*m*5 O7S>M7ēOx}Luq"`BV8 O-UݔuF>׷S'k32)_*sSMwS`yȧʪ`J7Y5}mŜFߧN5lϨng{ڱLv·]fvi~>粡 [柨bK'kqmr2%c|=wƓg;q n6ɦS.z~t} 9eC͗K?l2Pf;|m"d062׻mGot##z}r'wpwYq`<) '#-'~V tel=WMc ` !=<6c `?ThM0Sȯb.?yxa~̐ x "kr US.^*uq>ܲ)u%PO'{43 j+Kzñ·Lu7M볧{)VR[|w=gI.byMA`mmG0dWtΦï#[˭O)!&B' 79@.Sl W68t'붫}%0g@n+._?Ntkn6u].Әmc*3ѓX q̚ Mh÷MǶmekٛ/.<EK'R痋?P9`[~~qssCxd~xʰ .ڷRULS S 3gL6 'D:' 7I@Bmly{Ȼ'.)/J[d\MwNeq8:LyK|$?[ f;v9G fRl !s{;se(=wۆ^wNțv'_}$NګGmnIbk߸ M۬˰x$]@\mT=#yAKn.;J!F[6cҾᅬi=>٩FP?GӘ]T^t+Ẇ?L˅a&+Ƣ.Q_]"AUn3ڶEcVI3?6i.Dj~6Q; elWe ʾhDŏo;X@lmelJ#ͺ,bx4\ ,+ʾWPfߥ }@vt–!-7!Ծcv1d{[?x]Pek^PڊA  E!0_8=}9z60x,\F~#)ao ]VuTg;|FuNKMh=\e+6a##rZۘr+ֲK﹖t^?ltnIAm72<] <7v݀mr_ oKtіUl=wq]}􁖌\L+YrQI^UJ@>63o8ө"zşȇ4J"<^։05T0d6 WBέl/Kٛ Emy9%x<S{=~08x=h[?2ċ|uƩGM؉&L=O2ځ7O8Uxލzx6,~61򠎵Žkvp^eH_ xho#Զ.v0/#}Vma7ڷ|Y;}TfK8ƌ Q^Z̧>#>QgGU5zYV`{kkxM\uRo: ߻}9L}aoCoKkiJN P@/>IlqzJ ~bh: OKO;`->yfh_GSLqL4vu~lAy]aoیJ;C[c'"<]UFT[ nMeV>pi9xŷI6x?J\R qcXfL(P{wricc_9%wՂ9?*ԓ\9U/O/@i̮C?=:z նӵGcw.Ppr L p!dF 0@;,< }ë<_TdTT a6:ٹz8 `Ne5Ԋmx߫(;!; >c62 66W-oGnϵ]MwÂ9./ Q&OЯ=[F^ FL?xn?l0#4THJϢu~ P֛k4uФMEcSVCrVCFd' eg fdS}+nnohdO{mb[ad*.}uvpG9GȎTӇ&.ß^/_ܮw.W$<6O OVz >dsAܪ`pe@s==/Oo W <*^f>BW cl*TtlA.|VvA3-C(Ʈ,!|R@mgS[?1rrWQBwuig $7ŎN6n>7 [ \|u7kmj$xA Ɠt5s~jj#6 gry]^b_rMb-xcCKJ|Z >CCrL`Tp8Y>Lk ڶ[)Y:t.1Țdʓ !w 9 e_1%O}bN=; IDAT~7hv?0ޟ'I??=OwP WH4͵r; flrt^Kl*s&vȽJ1w*.ͷ*@;zyin_2<*leQ&=]+@|g 0X[.rTfmcz`{N:𭭖۹F7 |NM 's}GnuP`0#m{5h[9nж yhO/?֮dʒbRC <vf!~Kݲ94h%_o}qA5c~0KaI8y#ş׹np ΅׃]+GN?۸~lb"*Ay[[>]qbB6*/}w^.T[9 )̸mk[XO}}FJ)O.}!!dno7nN!r#o0%b>E{mnbFġsݞ߫[vI,!~jGoڮ]VִceOBԔ7P{\S NisTfTfyaޣL˅ʅ.nh|Ƽ(lDz]4"8-Q&8BUC2S n\D8; eh~ Ivv y+&tJ_߯FQʓg< _(]6l'^ VyƙW ^X!_fua@\j76pmS V@= ޱKm%yCM <]Om9ŅYorж_~/nrβNugTD)@խ]8f 9o\ضь_5%w攐gz3/*.=xR%@^?<\ycyF\RƖ'y:M0dvLۺg).)Φ3.mK5vR=.Ϊw 9OՃU?ϓkhz ]} !Ǻ ϸ<-NO8-ƬrOB*XVz5}%o!:[Kr0`jSY텧X[!^* ڏL~hQ> uvM_T^Z q`\;08@2<_LGPgk>YGS_䷗+!hrÁw7l͆!~Kݲ{{JVA.7u+J0~U8WataW&@>O\NЯ'׀X|̐PE3j@>tzUy!pMDu49wv_\%vwk=]Ol'QV Ѻy$x?wJ3nGI0$W0bӘp:8s0u%6VmZrڎ[\h vUPS9u8 "TsW!|"4 戴|^X7JPF;oy -| [ޫ!JG^=|qf `rLcvrk~ʌ aO#x W>0.iM~/bwKBQ!](3c&tJ@ns=Beek+Y޺=4F],_B_k/|iμAֶwz O=^9z[nB6|vla{ Ɠd/ 7i3oժ8'^; ؜9 buuEoڷ Y8v;Skp;`5r[I(BE (>볍W\gkg#<~緔g;q ^o!+7w\4V]򲼜yc8RԓAPCB9LQVW}ï̊4q | erNjw*!w 04 y\M[tPF@[}C?/f2>1\^Ѯ/Ux O%Φ2mll"K k'oZʗرBjq[ V 9OZUm@~qxAO9?[7ye8f{O|o~xjq:2 W:yG@H~:6 OnKQ+tH9`t<XuPU}#x1a:"rrFU.e61MjVq0m&Q=z:?h[=r:?zgnyB^v;[aN!r \`A;sBH92'ƨFF?QG8_/SRP,0+ A$W\f'xSpFaVC! _uȽҮi}KH翣`禝I4<}cx9h~r*3Or,%$( C7;FnnG3 Itה͂?S,G 6DTzҔ'HgoclBx1LcQ~RMcF^mƬ̹V9?V`.@ -[نGCi4mA1=2 +Pۺf\Qo߻M@ƅÅ+kx:}@ynH" >cl7’347 ߂wl֗BΓƠek#sB%7 7\ְ%[OX Gg>Jۡbqu !wh9wQЀ]6^-Aܦ5R6sB$#6bxz5u?:qmk[~CP_z\O3 M3o-"jBΓ脔Q/(yV?:,#)ڡo0kΰrYd+o̟<ð<ܧ8ݵ0T9:w#Ikh"[̋ Og#@mŲ~X8qmҶoCf{hB^{^ﶝ^o} Мp7!|5|gjR_pzi wMRm$jq^q"bٵ]Mcv̦1e?qi$PpVn⍸'UxxeɾA:[+|=u )[ߺʌb2N.J%aītӫ̆:)WOQ·Zx )+c@ٍxb֪27ܼp_JUdtYYH9Qתҫ>l>qqn3lZWܢ\}=yׅFW9K$Cq1nTti~}Q<2ڀh_%Or44=}MgHx:uS{> Bvr.{|d _OMke[s( dQ|+3@\B_Y,o*1ӹ~o Oc pZ`D&zi6U>?Ckh7 7Uq,Si' N+ģJۺ @y}ͭz38`@A~G?2B3`9Czó qNRHj4biIs_@gև^y;]Iņm9,<.|hrT74f@ΟV +c 6m}P"\lQyml +5p ڠm;ĎbjWc˪(ʪqu<ٹK8WәqSMwz#GwTSՅo-;'aͫP15,ZlաPcORMX^cSY_񫒾4<]>e-O{Aj#+gԍHlsr[E@J@K=]@o S >%R!ڱlr9V-0[FPTx: ]O!V6wl֗@{8ןfR >&j `؁G CAyѫ~ ^q}ĭ}} <n1=+/& -~v#V&/qvl=AA7 ] vLc}%jȲϱZFcL3Jdy锆ls^Bԇӿ!EblFXqf!;m&.j3:q1r]1@ Y:?&d _3?Oac"<?+(d{#~#`IKE!pdS=QX O1 ƘBf+q=!k[KwmYvywě-0;\\3a8+ht9n! C7:j!~v/ gPyhy]?Q j>>u^u6iq&N[r و\h:CMcJiÂ4Ɏ* O$@6'c-!ye C?X . F̎H<r FW;~qL]-B~Z൴x ߱[_Y 9y&PvMHl/ dw ؾ!ꠃdmR.<Bӹx\xSv ҰQ;ڗkO¾äd5D m/YQe xbzU6lRewKN^P!46%M&dYNcVyY?q]żg]_zLA #<)/ e CUNc'W(`| WH~⇕7|>C?}8=ml .ʭTvCD!!S$C^lm>T뼌4=(h˾cm@Aw}}UA|d­i9Ws}sԃ>D=[mB ݯÄU9_H>SpΛsg ߮ XܺBveq{D*DXy':cRc/*n,=ƍ8 Ol[Ϊ jɯ/C!_W E|Ao kh[=xc1.@9D}G7%MG4;mo .lp &\.gKo58olږҹPZ#Uvj!x;r{ PyjYHdU X ;c{߲I%ch2DԿd kK`7lжWvȯ\ӒP%M0#[_p D[V9ȧ~_yhmd@1\2"Am^,oS[ w\rݱ]%k#]xzŽvc'M&dU_~lжbC O#Ƨ8sp(=Ɛvw\=Ԧ|!ui IDATc" yu/!"<Y2<=¬3^~rkz) 62A:Q>xr1!ie| rACremh|6@tH2}M,ӿ)Co'nc]IF&8q6n~r*ɦS[˝[om{seYpy%0*u8aLa,h,@2 :l^[ҬR1HrXxz]-mG0zQ{{Eri #Ke]~m ;xz#]1$ 7E[td_6'kco{,lI1;wez .Lc6t Q5PCȹDˠUSˆmwC Ƨ[һܟ473|@Ͷoz9[uZ#sH P>f U[e2 kO>/B -Q*(KS [AlSo*G O ooKX g/ў笽H,5q}hBq./eq Bg!ːp\A\:;B\B4fӘU FSx4f[Jԇ?ȭQ@>̀OtRt@=-H*Jw~u@  f&Ѣ -&zX ȝw`2by(Bb3v g[aam,Eid:%kzt tfU?shNc @V5͛/!l^oFڣck_U 6p~1he WrIW萜7Bz]Qװ%**ւ=\>$y2.&!ֹG$ vlkCض% o[C|◹|ć?%Ƴ{;sҀn^i_Av6c/(.Bs( ٶech ]_`@YB3>EWLWʨ]=ur Uz*OJ[D~u&&XL^vU,&JtBա.]\Cc}utn1\*<{m4͢,h?jK˩ˮQ>4fx>ygӵvh3Ih7ztmZfq vmxARM~Sx0*ċ.'v}_5zq#D()x2Z XvaNۥGnj.q6zqӱ(ncg>W%VSJVGL-<2tϧw0-#POrbۆ!]W[!@T/Sɤ'~Wh3Q{?PkrXL{+;$7ٵ_W(XP%M7ıM@=La ϊ$CW4Ոeʄlb ]0=Sc~u6rteuSY?lmy7E9o7ܡ<h[mgȱ)v-A wO2E!eHqgV'Ә]+^9 OdkW:{[4W74 m7|}1 1.b! EB 4D]7]Ӛz$EV5(>TfSP>Tft38OC[+/yӖZ\~!AzLSH  !w f}Ő\Z])D`|‚8ֽXޤlNj6g,<wsGg;7X 9^aw`2?@*Znb^Ƿ{&Iw{Y%擅ijh4f;tAږaꥧ|lezsw:sa2|M^BWw)qi`k4 vi!ΣX2F}jHm!(e0!b6^" O1c}9@ t-nM`ܬ^SC,UEyy z(E>s,<ygȇTFxҰ 7`+d/tNg R3a6X8;5޾wUuIZB b7G[vSmz@e^LS! Wz3W<+.K#)oT>ӏm/ Owr ֮ωC*<pO,`o ^Jr絬m=3>U^qkueԃݼKnO#k@su=9|J7"l<#}-ɟ<myLehU4|dK,Wrli'>)7SOFO_~⢞}?`uxB7!W7v nSn.mLH wz2Z%ޗ/b_g4H2+.bJ)9_ vįqaQdGPt2<=W˳x-m^RP#"@,ĵveu}4-J{ /60xu?q:|lb0vѨ^s~*A< w&4ۀlײlc1}߼ۏ##D0a+κ%\y$_[~ {{d^)N7S" t9/BOl\ DY'f޾U+;0Uר^lS\V^cʋu|k.67b5 EOb YM^|:- ..m҆}Ϲ!`R Br<`{{I>lܫ2}ݼYua5z:n^t" eJH7l#!^@ʾ@Ta)qeohK}*dݦ|@˞AOOza@"ϊqˆ侭yP>T1 Cck.]pGC|}]kk?n,8hru&~był~wpsCx:+G'h7-8*>ȃ\l:Gȕ%x+lar ✀|G ]Lqgw\<<.llX?wE6ztc3` ׳r4/{E n ]|ۼضw로g& X/Fo}*Qh`|x@@.Vim{tp7 5U]Uɐa*ur݋CTn)cD@Eviu*;X袛2'[NaƇ38gX?qQ&ke;r R@|@Pyy ȱѐX$mǁ7m D}#ⵎSzr+Q/mU/3+ߤӝs? uT!{ŏkÞaQ;u JkR{um+w7q?T!<#nMf/iT *;wrmۥmtiX ǖccp9 coZߊ訦$粼^U,ߥ2>?4+J0އ*8Hz>-^8?pu)^qaж~zNH^w{s}&dh:/y&u(h W+G 2LOH]Dx/kV`G| fk:H~랋](k *2=2Lu#oo|> W;nk7:, eDu@p9=I7+ >+~cWZy􊟜8= Vz̅*v&CIWm(GPK Y7+ywho7C Zx0H՟/^^z/Q`ċ<ڻ/8ͯ)GWhH֓] ڠeXz}V'~lSh| ϖaӊyyf\lV1BxV__u n[Y9KSu'Tf6rbAN̨WXAި٠m'WhmQf4~dm/j}XX=ywn Ǒe[S?Qںxz_+@|E2c,TRO| 䭽>;CKY ߝmm87 15cc[>T3޽|_ .l*:Td,NӐrg[y;ua%\>J_xۼvtCJ4I]H]' ruUtNò>zjAږ$$]LH;KӋq ~Ӽ=ɯO@̀i1QWSVh,]`=lm08, ضXA6rvjxF.5UUVj^ mnzA A<]ǥ+~tX/\h1=&؍,R>^!Ɣ۪GNԶ䦔.ڪ\<7/% ﶌܧP۪<ձ{Uy+ul[Cӯܱ_^'Ijo~`>2K Eq0a.icQCi޼+ҺR7\wy( 8*T7g)2{`9eau 5+t-{i6&' Cr*3պ(WJ7Z:hu:WҨW,^]ʹjzZ!/7GSWޫ^Y-2M~'vPuma߁>b:]<]d,w0Gmup g6 ~R=H[ wsl[렘ϛOw/n.NN+$/"cK>2DUIr.ژNw>pU0hѤqߡ;TOTeez/=ՔfWƈ<9wr3>0!}О Ɏ$Q2 y`鳌rdu 2dy zI/Հmj6GM|EhoUuH{}Mk#b\0P n 䶒mo.kGn[?U!ɐӞpRz 2ɠ奲0*:N,oWꢨm$5 \`cd pd{<k_B/T[tѦ]xM CA\eשUe<N eB;Ӛ]D[*"aऄK-Iu[rAob6-LǾ؀aPo|KCPCc76^ES%'mCY+·(ա8̰p%ĩrM~rp }A.v|]!} 1ycxmˍ%e+vT龚p a紟8_*٨otQt Ѿ ׋~ ) mۤdAEgiҾ +W[e0u*8ĉ}| [%_Q 6IȲX 3ow>|%gJC>eiMԖ"XW]!〬ԌbCBNe`+P ;mq9qpsmFA S__$k*.tyqU/8 lmz#?_f'zwcMPiڮj nl譞 Wxqx7+.bE 4Borw+~6YHicY^ tVKj`vtVS!WB!wWɾU.9aa-ͻ:m@#8ۦS*.p m+hZ}ɋ=x]ǿԻds>w PPK?GQ_/.+Haa%7srEf^yټ44 S?FO^Ҟod)HX.vy<`^o[U&?xԥّ,Vy` o %K '[zԟ#*=kːk]k9}]X!@8zOr@8]pNa栠}72Q&8)e[NyuqOߑ8A>a6{{t|D37dYf~Umo* @exE]1h,S)T]BEm f>uSaAǶ-i!D/+dŒ!^A8/^WFYC۷K ,p{j_l:}& ZJv^-Ŏ꧆K}eH.8t:qqPqA ns U@0:x'zs*LW\Q,7WY׬aNYOjUXx  Ȼ՘U!b<4{1vD[u.y|oc9J׫36rz@m[h߷u"IpqV.Iά99O=`\.'9xAI9YK|_qA۸FVA+%؆%@]6tM|XnW_ve^xQsSBoBcжo=lP9 ؑI]k0\ 8Ӎr \Uqi?n٤^pa!Z*o_a%F6~ת thIJC¼}_##W\%') x9xWÃ+%Zhl S(ϳ `)ܶK-Z W%$c@\gGU',`;XY lk+^{ŹQԋt:rZ0Pb_LBV'.g]_lھdtZﳠtlC'x @\LgzY G+3~5:̇IBS12-k & i6m`g:Ɏ]u5oj[&rQmPA+o#S5CkpŽsqU>!$ۺ]yK_M&OŽ ``^..g bL+ D}V404,2#}^Y,.X}+~Xg}a)K _ֽ? zv2Us=eI*AG#r!!*}&1ٷnZK#wuyn2eHf'[V]@eqnNq޵7m;on8P/;>S0\ ug}leI +Sz@\ 2^q:z:xYx:9xa^qߺE''x$f`j]lD/H3/KU};qBmml Vrw؇o%qeɎ l y{AIW5I /o:`t=mm9_Zj{F7ql {:I$jŇ,bF}R.Wݨ'n覇Y& X3Aq+k BP/FA gM-8չ6TE|Y1(?N9 v~g l703Oၿ!#ݠs[v23 pjyΓb=)L]y`[6 :h*Umj6KWax+,7緹^DHlP-73gƙoK|HJc%%Yc }Ň0u7)eq|oW0WS rL$,擂A.~u+ճ riZvEI@ uJY;o&]j'_pl0׶oPO9r6~]%_T>UAyq<}K$"hڸ ]~-r|g2F,g1CrKi/b\Z$ϸT X'3FP/o.7\_q eiJokG@73iʐxEٙg]ŗ CӴz_l&d=rZvڇ)fԡuhzᬏFf ?cȕDt~r-Ȳ/y (pL*S7t`'z%񊫦3%-_P !|?qXn^fC L[\];]^(d4xlw(U 7CS@6UvBh`2dMmժkj' ض5xv)ptMWo\B_ ͭ0P~x@hqC5X [~^|WG_]ܓߜ5,b{ZiP_JNq'>>ۈ^lv+qFy\+@|V:7X[#D/]#w5ɓ'XXa9[O=ua%x3}\kQ/,һJB,>d~UUh?+Vd2^s ^rx8\'uߦufk15 `жv?%G|n6\! ['dž@V/+mce+v]-+tV=DMr20h ,gu&F1J֪-<.u~2;" FWA_mc+V3w|gc¯,/98@y_ cc=F}yw-g\-Cx:w\byHUxO*8?p+_-=;քrT~,\֓zy 4 'J;|E*.)9 mvx]@OSvLj ge ^nӾ.%> qCLu۾ϲ3sW+t vG ۡy鶒CzYp v`l&J2m ~QWxŏonIO}u24Wd֓Ϲ=AjM.ܩ/D\ׁʾô5+Io xl'@58#h/CgS/zL-"rEh4mV[w[O>0'iG _ׅ%=ZJb ;8h7\Lp~bGYNqs<98XNgrnu?rd z5vľu"u !2T8L* ~TH;e(-*J0i#@| Hx/ӹ;?AH^k(~9]KޡkH>/TW|cәq2pY+ "X,BWAzu_[tF_q4dvrKP8]<4Y^[BBS򯌗\AuI`ӑq7W?ܹz"k'6lҕeHz Հm ~LUcE3OS8DH/7޿0C&_+zP {uۘWc0+n'.z/*)nڵWWW}ŏIx^0 xϫBI{pGA"rn}:ىXסB crL]M ofa֠n yXh)on tdftfǿDGMd8X[ܺteY}xzIBU(q%2Bwi_|RT/[BA+2c ,:iH^5M 4D]YZ@cdN\&L ȹ+ l=ѕ߃L8mHV3>뎛k(8^,zqevŏgIf"L SBzm|#za IDAT+& |:Uw\6{߲/u}}ԥ2]`ρ#*8q2B9by 9=<})3[ G>/=0Ұ3 DUB1< Z[<̍/d(z[iȊ{=H@'It> E` QCzؙҏ?9o;g C(/ u!ˁuSPWCs_Un_ ùPq&Exl6Ct3V@nUMgV vyEƭ5x#~IQ;kP(&gB]m !<6Rq>_˴-sy~ؿ@^UH&7} ]HG{Ap6D7xW E9ղ^- C.5v #@l{D!,7$ )4'oSFP^!}qz*~hh:ܐu*9l.5F [ 2ÆB4" =׼HAC刡 ] 6V eA_Е{'Ci( k \&(7z+N|oӜ⼂AHdȁॠ 'f3Fԩ E~׺?y ^92ʗ!&krOaGxsʲj(ϊZ-&Ƽ f;/ͦOOU(]0m*nŵ>!{C;CѪ[1{AnOxabZmyM{~|DN(ә̠xpiW W QO6؇vzf ŢlYTmJ`6w3^x_کAMW}ihz0z4/}{Au Qh9O rb؀<4cHF"<[b5-[#:5c3vxa*4O!C WVnf BCAn@p$mD[ U7v'[T^5UǪqxr ;i6WrqI?8p3 /|DnpuP DQM@nuzQaޑrUz;x3v8h,K.alby@8M=%C_2Lz5S!nȽz%2tqӧߴm~W PM_$5$ 7QA ̚ ļPfL gPM>,>_)A|v:}?D>xaCu_߽]7.j 8XXoy^~W8Cj -&pFNqx̎(pS/8WzssC:۲YLF ZEЁyuICr87j@.W[Wk Gņ@za6~t^Ay92;OHsVJ&˺oml:ۢ@ɛu]C87yW >vN~R}.ټ⼲^q(ab*3gvMm*sqzB-}&nV+LyȥE' Ga O}yȉElm% /B-O2 >w0Nh?jw ~r &N: 1Omvx/ ƻ6|i&_=@ WI^.+};0n9|Kct:㪯x /1_?<$f+ݎCLȤso/^>4YuU5k"*;zjP(Y] +m! !AXn@Nx\]w70c}a.X ~'ɭ?hk}h[aRx2C+yt<@<1YiS╼o+@3rTX9pA9-~_Oiqe=/X^N)1Ђt~MXLeG֭+d @Su买E ͫ91[U_)"3  S(QO93$F+hyyKw0Wɛmu9H6i[4ku6Xc͑{+= bg20LUFnUzÏ+V9tf̊cy^9> 9~@nv#r.\+cp>ە7Ffhd]?yǡxoPYÿKcю nom~d+QCP G]MzH@X@?K};~8L@ g˶)9=:,=Wb.U rx^m"1 CΪ]^xbА' .ᦺH (C9ayV[ӽqnz2n"_/~Lar'q%F+hmvy=vG{RW]:u}ȴI,(Xu >|͊xš]s^qMP.[mʥ^q?qC_'KZ#r׾ ]b*nEg.L]Be˿&Kr#:8!鷖tfŽtQu5xM=$K׻@b2򓋷^F% _7% 3#k׭nIH0 33dz33x0-`a-f *V mZz}U9y##"##"#"ofFV啲z7nfUeƽm ͅE[{ e[@\ sTT q^l&gkœ >%-\7G[Rg]io}k+S1U/OO#esE(]5y_8Cъ:y-Kz?-wy ?okZ#:&-LY'\{͡dm9&K[KyTr9l~g{"φSmd CgZqGcr_yOiRqG F,k<8 Dcx#jЬqeGv1M{>JCX+ HfU kw(efpӷ8=֊f n^qy9?fDY w3GWetl1Lփ6}n$Xۃo@N׈ @|&@yG y\_&gNzv`3k=vG.A46A\Hv^r䮿#~.qa?[+Y 31mthd{6dW|<ˤ~+˨e΢s2h/X[/H~]*YWlrU틍D7Jq'[E (Cr6 tȲ kWɵ7,_,\Dc >ӧW7%:q8'CƮۭ*yAn(e_4:uC&7-JnC!㽏JMͭr.lMcJ_b9ς0t9> .M1@lg:dׇߙqHVG+گYܺC`C4mw/Ⱥʘl-7By7\uԑ.QX{s˽Ƞ.ʥuqkmla3)m4a˦C:_+~*3Ӂt$Dc^+qQN4@iI:k:ۂZd8# 7IWT`a*O8 J۹giu ߷t'󌟜rn9C#@N]Mjnx$PSߤ'<>-΁ ta8oG|s O_"Y`.=D;Khw=gyX#c`uk7MBwS^UFE:} EI `aI$m:/+g퓭fdҵG,z % 99/Xay6^Abs{y~ȃXz{? bJ4xX@Ea ӯAlhHO}.@<[. oiuS"&@vW#_ 1R:H0μp2BOymdN늳ml"qAd!y9^s#2GbY҅n'Xy .ρqي |{V፭<Ŭ;dYA=$C7Ox'!lCL7

cT|t#Xb{.2O@["O=soC bA >Sk[7.K(ݩ n#:5 &@.@cmn(c^q& LtIt r/uxq> NkL7 B֏(ØQr~._5nŬ Ȫ iG'uc0ȉf >uĐXkkg,q9rZ|A< \%VʬdmϽS!OZh eV.+F&oTbS:l5/;&)KbU_f1lhBFs~ V1Ms'ɶi=rxI3 Um~tLu`w QN }fe!yU;A0Q%͡%th`+r}ZSn3(&Umsz E':xp~K?8$#l?;#WG>\>hUC:pp0Կ:iS:蘒AMAC%W&.8"Uxd g\:ϸvP;\X/a1o]O V7=E<- }%@f{_<: '|ncR!Dӏt7FFVoqpdij<qGkđA>?ENza="vpO>C~+c Et }NH <[>U@ p7vVx8qRff}-)<of@g[CM@^!te q.C:*ѣe\.,x8kY>|@|\߸N#g?[zQJ~ʮ7с6O "*]oȓoP }Q 햶™vpa{Cr̥[rz=fj8XzQ?\kIU8'=S8xqdӅDz1$TPMՅi[Nm0 'w~cV1t8^o2|:!t'xx,uvUĀxA. !2\X m@y Г=`\Ǭ}ؒ!ã~ֺ?XG 1rՏ_=U*6Xn h1::*GĽ6y]pUwۆdkW4웭?wrR ^mIqq A,lTMI0A?;]ED|Kv`10Ey:&&]?ut#g3&i "RmU'g8qm$pjAr+򲎦\Z;VUlnNdϩVsh. ainޡVϪ'/@_f/,9UK?CU _ztYġ|&_76C@4Brw}ZdX7FBqi~xmac|S0m;uYt1yH+0 O7 űK< Bpؘg?9HwHWRd W#-Սt(@^.B IDATh3@lWƫ-*y[1Ż⡂(vB]7 >Ɂ8 IάxB- ~/\P^hw $썧w[/0S 1lk.UBtd(mR4ν4H 0mYԝ$a%mK.XFu gMcR;!B^g9.V cz +>hj] \n7͑L~3=5=qn/ uٚ 8#?=ܯz|2Dcڶ>9lwV !i?XH 7cRNl[1o_'n&@!*^@:@μ'$mK׋<.r : C^DZ]ש\l=\&cx5CJa|%ecd%r0L/p/mO+ L@+% |0~r%oK328?#'49yr W #E4O u%@Mx,|@^V .uS t8]tp<^:a#aeK >oWRC K]wgn10 1or:co}WLVfkg0u)i u^pݿ˼laYԡ8y ~F;#(9s=]YDЀБcynɒtjcZȅ)2bN=7>xݼ8skytE=k)c5?'Ga>t!tfqz|wtHc6.+uϥ8]{]T&NTu?0Q.8\۔3adcDEt45H@LʽuG݀f@Z94?mkA]axfd"o{fSo8a'{aTyv - \CN }mc@2r]%:|{DnjqQ#*|>/9, k@ڃ6RN,X &4? k+ΤqpzaeQ!L2-., 5@BXncwPJ\֘'uu9NboCn(c@ܯ+srCN}uDiFB=3Gȓ?Cч.8۴mKN4RA\:lk 儤pM& E/gaXS8Ƞq|3^k$eK׋0NA{ǓI0BR4d Wk{|Х왰:Pl?W 7BB }0E ?Z \xlXKyAMׂS uq `ϐ'~SODž =D{)CTkc:-aLc0 py C7. ur.bz&mdEO~T2GҿMkƿ"ټ DY6+!ꧧtM8ȝ6PүS=8Znyi&Til = Y\Hih gǒ/+UzTmm-C8[6+7y0@F6;bmr .q >G|R$kꊫ&t{a}N(h, ,m}ʞbȇ=fUa2!1+0<\jmH`ZuC;94Jݙ8/KQG>F$[7w{U<']Plq [ {>"j8>]Urҫh,{k)I2)g&T/M⧧Yt!' zqik#Epײg>juEW]@{Fù0D@Nzb&[;K:Q@ȭW<"Uu6R?%je29OO@a}'_E[+j.Lۺʌ{/qDF19*ĵFMͣ뇌u&xk1ƺ|Utr[ɥiX$T8H0Ƞn?WiҶ#z#^ʙrRBtW\p9Bj)TlUH% ib)@P=A2)qYX=nᚘ8Nׁ_Z7Ϟν?%ϰ{IF#̛䩟}}rŽ!GUtŊobv؈'ў%ïoVC5hw}A CG@& =5 ׮:Ս_O/]#k#ᯜݪ j kcv4yOy7=e(Lxx`6 '<}*s[VBD7݄(Q54͘NAơ:ē?zrG_ў Wt_҈o h65ojt%QVBt>< Qq+ Uwd;(-t=8/ka􄆭,ČGr9 Y:s,M!c.U_@DJ@.\/M8TGz4g.S;KE R D d} pAˑMr%KKx2c (&W=D{+!]x6{qiR%@4{$o:[U"-K^xu 3nf(Vz3NO2o aC6^r5ȋ7r#8TG[wqqN[<1}:ރ*v:?aF϶3U8^szlZQD/6덃.2L=ueOih:%q/9Pu0sONki ij"3kšƁGÒ"_U .{V ^_xeg6cBa5O@KG3Cru:]ګum9_6cKI06Կa8D(k֌lfQ?K<闒5伝9q[CgYnnp}9:=9r,]r͹xr<> pDA.\T,wj=@<+U& _&kK@|wO:}!7 jWQUmĶKϿy:+ !em7MFk(kR IT^q=3a 6RO6V,)֒!|[ޱE üD :ox_ZMurx9U]m MVK \ q=6E{]@<9 W s ;+]3 Yx:Mv%mеXroG.k  ڕ*DT]vT;y5rPb?֘P@F6:I69D[Et+_vOu\MBqm*qۗ%^6B&O׋gka0&G+ȋrH Ū}H=f]3/T U9Ac>8;Gzh|lD͊uA/փ [id5i(^' [)7eNCUiu O6 Y5lrPi=6 t8oIl$0u+JVE}Iٚq5N-%#6N!j(%$[!^s:P'H@N^9X@03adY4& 5epO[uM*Y {% s؜?1Z=K,^CB ޢW1xmf# <3?|D݃._zvWUyxʣwQ[)tLɠxS󬒭pb:cO;C5_5t+ 3.{U^2/8 v8_/l)Lb&g &L䅤n Y/};{Zc5S7 '91|\@p6>< vsZ'2 X&=xc29-]@5x)t .gP̛Dzϩ;;]zvW՝wީ񕃸@FB9hBC]l m/ !i7$m3?5ɒeN׋S?'äQ6XImJ/0g9ͲN⿽Rusth@nI5hMvꉅ*k0϶zTĪO9A~[A )dBe,3z ׉_'tNg/;U.AW5e=D{*DTh{w*֍Wh4.VPl, CRLV繩˻}xLl=>3K->;֊g R ʬ됳t1hy7 Y,gmH&G. c.9s~plu 59DxWo~e=?No*tQU="m՛Ng?֍!yNBӶ kW]X^2%L]i3V}f ,D1 OzjO WGI*F3֧Y@,]y)ӾЍ, ,:PzI;s m G~fpXiWQ<~뻜#;% o/ׅm8.C!!h/A> O\֯/0&$ocVYF-j Xzdtl^q'YL@,,]|56֋/m<:lQrcEq@= 2+mrr]4M`HG_ 76>(TS5Y1?\*yKUzM^g̓~gF~15!cXLK 5KAİ_]SAIў UDcɥ;.=x0m1TG@KGmP.c8[~/2~eB'd k7\q$]//B򶅘 :rϲ Z=葦yکkk)H֧هw46s:+E\\rtyN-m.s>cou= R2 ךד] ~ IDATka x.yŌ7D7o_{Qhe(j_|%}t6&UG "J(V!;kyrumߍCҶ%qd3ym6WW3_/P;ʚ0uᴮmNCreR]oM䅇rk]J.@ۈn ȵ`,MPի?)+%K[jzN5B|oUsW{IkeId.]v~Yw~ 9y A)U&pC*]>[;-v6 %[1e({X^S퓶y+ۆqWI#v<Ήd$KOׁ.,Cgl.E/]vз]( U O ˶._aLH aks i ʡnK@3R ~eE2_eR8]'׊q= 9Ƙ$.#7u, YcԒA Ze ]9@*UpoK+k}lϑռΑy@My41M&x~Qq^l2ECϨg\=%\aΫupE:otH@yB<:L> X=}I|%)y'smOKzy6u;N7cv+hdwWO;e46\ULgPs.0U 䰹$JSu‹ЀūA9MSv̵>P|}N dt}8 Mu3E8d<<Ô#~ @@ =D{ Ӷ2omn:`ScJPyBPX. ߐmz$ $Lewmv@6B׌Y,K(u  kl;RsغcC,*uT䆝c+L|luGHXwyhsTU'h@׆Mj>?\PC<'Nmm.  '!S]D9 ?8؏gh  9Y70]7[q%Jou/qU=B \=]$izRg-OK0 Z=!Tmrqcȋq.: Gu;%[9uu'cOS҃[{s^䱮Ԭqzh- E8}x1ӌ ӌNJ.p}%X !]@YÃ<H@1hj*1> m0߻@6k)z'F5%^,ZZ[>/5*z@zne3q@a7~Rl+@eu[ k%.~O uZݙM'VV Z[rVgϫ~;>lr_=;͘OE*i[]ALס RuaC*φmSXcP#szA:x01 2OS7[s鲮 }6X3)^zǿ&p9yvLi:%"}JohV%SgR8W@n#%u+ѥ-WF\epK핁'\ns+u))jȇ)1.%j[x.Q[2@Dmg>C>-D>!S!03~vwn<(,E=OHڎ=&(a9t6j=cS7'RjGҔC$J u ^ҹ݀\W\|I =Fu 9gi8:#:m Glćzb]Gܚ98pZTYVQ@eXC3GBubu +c:Bqƪ{oe<ēmTbh1eK Bzbos.ߗKaH\WW qX~}  Η`k _Zs2 @_NT,IH U>T5y0y ;@nҩդ&ִ)| Fx0J؞ +eF7l8DmB 0eJr䣯r4ƒP.U*\\qTd2' 'ҎU?AqРɹg&=u_SZq| :,SP;zYM r="WQ|XL/1a~۵U CUzh)(d]zr."5듨 T`n~{}ez+γ'ҿtKABb@xeאdHxK{9o 'R r|hyl{Ƞ?ZRN N9jXzN`n\+KubҶ 9C?!|?W TUC걫{/d~xc1 bkS7qM~a\F?v{xm fZaJ̀8xãBSϸ^ ^am/C3l `1 *g5rt3O#QȓoEȏࠡ+LPzPDUUӵ5^ytLa;# o!(!S!U9A=0/fL@ n#@%ڃ谑YR  SbwwqBRf[[+ewmDрw׵?y$\$<77 3g0! ,vW8kX\ :с.6bQ\ ![x8PU"9uTLh` F^إیw~&lƻ\$o~@߽ɔ&]@Xame28tاP;# {ziwzvW,U׼m;[{$@c3nt`e5&#sf/J^U]Z=RN7˙mg6 WTt@ s-zQ,W`Xy-?݃=U#ùrql"vlds9l r6RstʉR r/Aw(Ϙ;[.dNϒ5>Ur+ܺ^BWQUzC.˻řQX\70]%[R{?ۣaD&,N᾽&d{` [a,87[7Ay(orZր{/bZЧ@r ϡ\x'r a`l ^+9S؂sG7rxE ֐;s|0&Rgå  ׹>UcU $`<t/cQSv˸:BPiꡁ\w~cbXOZ/y^.8yWzs;H{^rz} Uu.=4w镛/VUPlmꡁP>dH+Ѷ>z#i Ʒ5R'ٚqXK ,qg@.{L5@q\!`όݣ8iԞv!en#X4-H_jވu>8(s}},u#laWꉄvP<IצBp11erC$' W~{QeHǩkwscbxaR6A T6谑80"pJ< 5'o)q N%f%Zeag6 u@B92v̨>4#}uM9!j E7*W)*9tQY /uCxޮxa_&"fkgç\¹\|߾ TPM" Ruڶ&lj~wg 6#8X59W(`6 d<Y6+̫zbh:4OܾLZ2y umU<⤅tg@^\ 'XXr]r /w( t bϺ2 S{*&!%NcMVyé;Vv|@ =a>so8p1W 2Op([Vp'~H*mMs!E]blxjߒ~c$mDV1ӣ˛+q0m~dX+NG<딳6zSs@2[˒qӈ IDATeq!ȍ8~a6kak Yξ8 e@"YU/gY鴧9{plH޽. gD'*4y}l:t ਝ /&^T`l> 9x' TlXLLFG|oe\A \q.s8 Q<_+:،[50mxCk.yj "3Mx:E_ެv^3LPn0uNG)u3(<>6 υ'Tj֐@q 'Ey y ƀky8zV? Ul飍M uJOƛ#HC?7&7NN_\>_&xt =5}7 \zx2 ca#3K ?zz}C R! }x`֐@';۫9wT86k_=* U .k3#ȪoeSW@ F#2҅ʝFP&YYi⿵t*ڰ.@l6 րgóllm^mS{2rr]姠h?!G0Jۙ_dm1~1XBa#f@\3OR e޶g\čgK2׷^ WfkY|t[7 ab6[ /rx; 'D[\F/{ue@w ЧkwMR&t\ @f>GmЍBUt*l?CG>cef2t l}xa ]#~@e]Mў ʼvJzuy }uyyLUƇ^gD/\i,5%e!y j`7ZY-FPތIYfȴn ] q<2(~3xMt-7dJ1wPD DCqG] ז JaY6q8K>TwX:W(xJC.}! ӮT]xtYhrP 8u{L3tQZʳPx%ƙ*`ݤ"2@ /g3`[鲲g2 'tpj0`m~7A ejrA9}S8IJ@K 3oˀ\]gYvKl'}% !>cבsDʏ/73Rum򂷭JȺ$~[rhבC[I{Lm]\ sĚ$[京@rBTd" v:XJɀ=Gipj2[mv ysldX:wGhzE'=DIG5_~r4g J`caE˛r 3. r_VS6uϙAx& |Vр1\\C^]gY֑}b1m 4]x󈅡T;̈ƴŻ+PE>L1"@>2>7c̪kn Y;8F8\\vm%iۣ ҵ#.8/a6÷Kso{˿Ȏ^C{a"7! L sn@͑X &zt@cq,* FN{ V0imv@$m 'W'#>]z`rMOLv`I^50b=p ī3InG#tQ`/oV*:/rBO ƥPu" SOc=3"f7v#SX~ hvu3_ A/Z@%Xn#cpv{hp%6Ix2"30 &irC}/\wh? ( ѪR֍}`n`n 沚dmIYu!^HY3_8]7Z# ŜG7'@Հ<i:rkbG% 'Z :1)@ȩ.źsA.Qw7㨫+@ކ>Xgksp|m8qu߽/sb7ꊇA쁜DlE Lp$m6|2g}!07u(YF6../VCz`rM0bq5g#0 eJ( n#I HM,_f罋F֋8øEkq#1[7Jnzi93 )l7osr\"sO5:;X9p{UeLzo²U==sr]NB\>c'HӶroxszytŰ7'iKoK h? ( *&ottA|4[zh`;.$/3Bxz3ܗ7[J5O$zXq\6 #N׊Z0^=sxW ?.@N= (@>b(/yǁNsh@޴3bpq%xR6.OC:8sKQݾka>6Gɟd+} %[1tvXI>_B_] eR)--Cvʅy©W<!oj@߁yTrIW~8:4p7\)=I(Y&coyrfLaʚpR Q$Lk\%E"$ӸirkɈ`=K=ZL٪K/Æq)T]o6jp]@R%gt*9٤!syݶro lː )g%odG𷾊.=D/C p'72SvV,ں9̓8:Ax/׮'o( ;LxKqy3NXx:ͦ>LC=;΁|sS : ge*"ȉ0^ 9p*isyo~@^y6K 䅫~Ul ȕa=šq6<o8$h+<7WSCtq=D{I .iA虴n V L&j8%Y#C IWkxO#! NU5hC)AnqC>5>Z<^u{䲇<^3uds#9Q'Vf[.N.&ic=Bҗ0.x÷[o?#DaRV1 ָ)Uz6V L:PlŚjA|thNWYK.BxEKhsaeUƩ "lYfuxGGz /1ap;uh'Vs"q|@Nԝ ukG}蹐t)?)Cɲ{ڶ7U7_wJaZ$X㺊p-Kh;uա޸͸Uu{h`+(4k9kg(ׅ@x}]WgDR5'pK$L_g!=8;>+HDZcb=rJOA ލFd4H3e5g*!xAN| >ܼ5nSznW1&NbuC[]p.SĐvrz. IW=HXUW{B[4yoX7k84\=doȅorZ!9!7⿌`j<]r՚pC^N{ZOpN Lo) XɲlmAlBֆs9z!W%Ak\p^Ct|`4.'/0ctlښ1PxGa<"7epkNFrz([iuxݶAt^.y)NҰuCA9 N 2@B. , 0+Văgl,kjMn =@na h&׋x|6tw7k~dm" u{^RxAp^CtR'D#nwUPt49Os=D1 ]7~A)W I/W!&%o ㎉_3Eq2@>[.o@NFm<ۇy"űc 5%GY !9f0+?I%up"`m6/ '@r,uȱllν<]dcC\߻ѵ,%=D+Z;>b_~zu45OqUu4QK\oNn\ ? $o$݂qAb᯸mzq>k—q6:i0;,봌:9H,9mkEp}$prDQ,yT=vɲn8cBܴOv^l@.]>cA7 0֎7 y<jUzە5h,>4h̞@\d ^N8yě˦N׌Gi:zGC 3E KQ MD1#+x\ CY,>Yl_W0{l%chIetzy. 4iЅ!_AP`!T} cR{uJ׏$nQ:4}m%7+s=PHSgLWug 9Si*90\/+ͼ3 IDAT2o8+oxohp~ߓ7X a =D+=D.XmMFɱۃU.=4ɹlUC[1I񢇺ZHzqǫg;bqԩ@tVl(grEU/5<ճA\^ra@onyD9XRTo;#>f`z^s9tʄDK,@p2 _G>F_{ˏ,=D+=D.!(^,Y=4j %c 7AIhtp/E][YihzoA ѢUrL\Qls"Z@>W=N91reӽ7<I2o8+ך壯npfKJѭKh%! L\6cIr6A,cV4f@P^i:Wڌ"T~čy!DgS};rx5_rGD;"/r,cԓMmIX218 Q@g^\?ЄABݺ*Dcd0xd;1S N.o֍uz}hY8W\7vO4-$L=>.%0NC\C_q:yڥ(̋ =CJFh\3 V]lҚ˕My2nMOY6Ȓ>JO!@{ɕ7|pJѭK>wۓuݸ̡i(6v\( n#X'OAZ7ޥu&O; W;m x<mYq S0ʘH@<} 4KdBuVloo'jCC&hr~:yC/=^!u!Gڅh,nn6\t&4pAjcxoib]r5 6AdD⾸y&g%|jyCuGVAbL4(:Tu# @.Oא@nD *ۙ7эqZ ftsPqL~:\xirr5LՓ9F-J׀ 6B6lqr˨|7(on]\?%{-e&{CtDcJut*k.yZqN|6IBF p_2JdC dgYGνa82[r܄/NOPu*䬌YZ φcSPS9WtHҺ⊹cuħ*`4$Ct۲%;Ѱx4yzh`+tĻ$>ak.\DTgXD{%k|J׮ѥ;dCR7xr'|4fY"s$Wr9T ߎMp!Tv6PsQrTh! zyS&|!=.c!\$nی趥vE4kK  }l։w+3Tlօ:D gfsB rSEYg#%7&9u3_vsDs.&8I݆ M]}Ms: 8GI D%L7<t]8+e&*ZH:k~{+!Gzn[znW2+' ƻ{`9@Y7h.d[d%+#KY&VDe;Te>$8$UrWIXeÝeF ggw}{Nst8v7psU eCd'>AxP"ӛeIo сКpiym{WPO`` kūS!TNpu7 !и:@ssųE#{{p<).ҍCwG8M>EĦ{@Cҽ|U=_߶,MUTJzP߃1A!]4D+E¨MJ^ԱCȖ/VX2++g|=Tݚ N)2aoۂq-s Ft6n\0`ffmcR7֏wbqUeu+!:l@?y;Nt7Լ|qCy^xa ߆Di.[˕r%otÖqtzVɛ]&Y/"@4ډQV  Fp=lk'o|wn ihj]+ -MZkd I V eS%2a>Y3B0>G,@jY|;du:}c!܌Nk,;a0u=''c@~49LRXUrBo2_XZުB:rAxU ҍ>OӘɔr\iʃhY{`b~+~-0Guè#Y [o8ߣVr(;8Mm 8=C7Z[ )G)-ȗmEc8c?$݃69~G[up?<; lUIXCtRtX4DKՇhY¼qvu?a^ Z @\"[y{{- ߾%5g#gPGgX) s;9No Xg*e;xȫ sk w. 5ڮ'| #oxsy±8פ8[PoF,UZѡ):,W% yg7ǖ U*U-U@t측6P 5o[?p|Q[E_p2;7 |7= -=|p֘ s]4?]Us+rgym|#L%E 4[05h?^:vp:07wrȷ+g<+wj?A|>!` 'A8 oN}/TZ4DIK魔4a۸|yV^؏ 8z]'' U7hGyW`^xqBZ\Ufqɐn+|(éntC& >l䏻}IrD<4=\UmRՁ|9sKB8zNa5^\m0- #ὡpO_"/ֶ89Z_k]'iN.Rsh6͛]4KO MxہۚwLӛ/+jg{{OdǶo_Eq#;cex/!3>Z&roYҹȫ q}DIm7u=}>dPp톤Ftd?ݫMQ@Gx1o:Ƭ>]'iN.g7՛-Æ* ."c076ĵW<(+}8GڣԨ73*'P=&/|z~ot[=^uK@W!xk  ya绫"m_An] IDAT,+h-x_zICtr) <c4oj u<8lnl__j}L`;wiizi+b}: ~/D n=YtmS=g]x.r c.88Kg{!Nr*pwrwt| #'}nW?x˗Rz+y9*W%gon"w* .m6Wm4ZuӴ6[Mn.w |aE S=YSΥ\yu.r>q#7@fAWHGƼ a~ wBi\o #o)t$_8Jm!:|jUM70342)I .cnX6mB_Mo:'pOY_xw:ݛW'd 7>?c:qLRS+l9u2t99{ߙ,U9;mֆ{;]&/oz :p/O+rE݆~<'<>|Ì:2 i+}4D'[ـSQjD-L'ME~\ +kݽt"7> . 09À#_&mO~ F?Slyygg9E^e _I8yÁOKN=0‡N^=.oyn%ٚ<= GN9-.S.1vviN.rՐ}lxIxs؈.ئA\T6)/O5G!Sz3cb߆w:yqY6q9l8kӛ50/< =9gέ=v6B3M{7uuB冬,'}v_ '<@yMM+h{ѶV@};^r8ij SȷniV(z»Ct{ 7${n6*Fn炛g^S>O8>b`vpޗ;&c.S7z Gl@JrՐ)2%qCL ӈ$CQ eSd_2"dum} ̽7/?F533d`ֹ^q;N Ƭsק-ea>V -@y XD =}7/|^p=^8z|{.{`Vw W>"a`>a`G>[|H m\i]287S hYlQ%ovAB.Vg=pݒ6*/|½w' {D혛 {VW_k@m+N' 8 [??09Džk GTr7zq^pv6#!y`~o{{n\p E8-8Uv'x&֌-vw6s n, ]287S*Ѳy-7t3mHW*P?8 !ZMqO 2' <ń utϿE@cK٧?woE&usx!aE BhGޛ9S/l@V<y$n8]{.{ؘjcρܜp/@}s-3=Xz3z7ˣg/~"(M1e%CZot݄`.MUCfT!Z1 U7Ƚ,em#{Pu.cB6Ag4֬Ů#SxDiu6iyh͇i HSHCtb) p  d)0o$&Q*˲ m`A/C] oz3oyyBΨy:6Ő'QNO~L.n?Yei7Ⱦdj+ uת(WܓeP nYG!0%5vj|@sb g_O_n:q9ss^֐?A%mϗPG P!$alG _ny)@e8s}O0wvj^7t}#t##ho ypnB|܉XWr rf`_-kЅ .ZWE011FJ\C>?YjRbTfB)2mUWxG{&`=zrRoϸmӽLw!;AGpqOK/Se[N9&i;0n{UӅ9B/@+k,C7=t,Flҽ2yǍooCV*6{(]};v\jmu Vd) pꂗT)RCFVf ӸyT [ǣ{MeX%_07mTGQ9IzTD[ʽV=_"r^r6{sc B99Gv+Sn;YE=p-Lq_K7%d ֫t=WAtُ8# ЃqB*G svv.1ÖcRK4*ĭAfE׫6$%^0TmGa/|8"lOZtO(WH{d+٢_<-X\{MK&1ĒSnaQ7 Coq G`#r_/PT^_U`z)ySw.sɁI^gȩ%ćl+ p-ڳnA5ƿ-"/)CM'HV g K'kz25ޮD9*US؛ío<gsiD{U{\P3[p8'Cefո , ѥJCtѲ \:أJ^"*b $6rinݔ+ PG!衪g0'V-l؟|pǯFH)nsj3 [*S89Yx ,dv1qr9;E8D#,ίs*侊vޏ Xl~*M| ߡ'~C˙"!?"'{mWgpo^l/? JUn6} ǪԢ;DYƁ`>Vs2FodBb!TiNB,0o o"4,8户b(7"1/\<)#܁J@v%'x\zAe6^{#\=MΟ##x!<8u %;y]/摒g U͝\o8609<{MV$GnV37o<{n@ Y÷Tuڽo?mkBe㓘5Hv2†;cnePF9*\-K"pkb*"l`tE0 IWcc]\aXy٢L_|f#rRikY-} ӝ[vVrU^`b8o;a2./9۬x8'u'kyݿhW[r ;fU5{Mm&Sůn+* @9q̩=fc&1f  %^Vf؍IƋN"9Vr6h|zm;#uSTl)?Q]yFe-_-Wss//zԽ)k6{PwrSN}8 [n8!aXh:~;nząpnW@u>p{+^5UK876}.RyUnV.9qNG RP\i.[y~C.QKen*xEVN[GY!6]0ub{UlQ<0;pLxȹ.|31| ^.].ڶ{SQܿA>{n%t+c0< v+(w9/\=ꆳ|nғ$ 9ٔQ6~BZAGdK۳ \t bZfWI7HCtRXǿ)W%HG1.viOr W =`Ի:GhI-[pH7A.皸pmh -󄻰Η!+OΧOs!|3}{cBӝo «/ruASMX׽kmڶ|ޖ+CggΦEz/`6VQY03d12H[y2iK#!Ÿ\QrCCVϴ\ Y} rzRy[i֦7PqnMz{$I`ze`< <@8 񄯦1Fi;D_lsՁ;ͪ@wTW>6B`=V3BĔtAxaa I\I)pBl) ȕC5,x>/zUNZœSZՋG|aQS, 7睮MqB_qn*pv!ŵp6$xoMsJ渒/4: |Tr' WMa!p]T/b.$6^wƿ(X1qG3N1U&d.-8̧ߟvtU%#*Yl]ܗm.ʽܒOVS IDAT}'=; 0©F79IO@rmt},5@\"@\D dz2T ޫ8؊'_N?~:yc-u~qhVS.;]LˮIvr*e+n{o^V]+`j@m4J+6 a O2V[+\j,^އf”s:$S_5JD2uG4WYi־Kh' lZڷmIZg{t'=g`~f8$eܐ;6 KݡѲwNmfkJQp\d_&(JOn7CUo73move5 ގ&xg+Zʒw;:9sWѯUBp!rv_&rq bEׄ︆bhݢEVf(\ RCtRwh(ZYċ/uLEA^x0TOjY 6 gWE|p1hIcBU,65t˵p _ _7s׽b5włŀ||ؓ˗Czi򪁸P?_fI-m {8W[6MHz |cL^4RS/0\o7E :Э;y2i׎^WXm-X`Y2TeH>)év$`mZ iG.о} @[e6r:^ω]1^ys}omf_{$/E0);4 RQ+ )ܕȮuDS efoq SA{"{a3&}zDqY 8dr'[/=VN~HTR];COS0zi -F7b!ZzG_51ufuQսǿOoVǼ SYg.sS'=;נyyoКr';͋p!ϒ*YKs_PMՑ!ـΎ3es&H"u!NCtTM>#w*,E%Ơkm⪺W .aCU _R.~֩7GIMOeCvӣ}+a@5joNlSCwB '=݄҅UmmJ?;ccgʴ.!tdh ]/5e 51cT4K@\D~ݶIzU:H|fꪅKpٵtD#ՃlQib R˝6E'^ěgB?s]M6 S8 C [!^c,axPUq+z&UNGU+lsf_s *z#|o{m_i/wv̺/ n=h#=2M~Ryywbq0 u.H M4DUC,f 8](/"$߿<fUWē = dr5rq|eBӿ;GoeEi/wvkY!ϒ*}Ka Dyn 3 {3^0 ?r[79(T%{b5¥C88֑wYOA*qO7|<o;YŁ;ݍal|!p2$p-:O HXUvp.T_ UkkfC^Z>va#2![y4s_8yk˵;q:CRRA9? D#,p,vQNz:︊iƔXYrʷ<[ޙStDz6=0a]o ^DNa:k>Ycf#QI٢:Wa~:G0삷v];Y:vۼwTl_E︊НVETR:zzcdvzx%SKHGʙ¬W ӜOo[Uoͻ3Idƫ٢#~#pB)8LY!0Oe ;n?|MɒujӜuKͷ."7<6.Yp `n`ҪʽFHD+uSOaVu(a_Y~L oX?ϙ;АXMURK"ڞtICUm<<) !-30( 0F4kA=d4ZUqcM?x]qk I/W&GXD. dhΠbm_~{?î*6ڟg^^7FSY];Z8rxd㬅l 9Ov|`p1a[tQ8 efOROcQV {ۓMqkO^z~~ِXqVj_jWa`䆛n /֪AwYM{8]sYd:U] ^Aq3\Uq-(/< \}k؋Ha b: Cл~wץ4p?`hWc>]\xf_IUW^EBy^r@Xo48WeU b @\D~Q?\uf,أ%4.5$=o_Ch=ㄒȁ*d5|으W鿫$*XIm6Nwx.2dj#;ɂs]-rۼBYڰh[I5[S+/<+/e{a ![TG ya=Dݯ W>?ݷ4t'Yc"vz6:BxR;;>Cq# g֋rz-z1{~Lbwz44 춌ӗ4fTu4r*q}z# ZMcv-7e._?{~/1b.;]-EټJ%dq;!7 7&kWZ.]^aeO&9VRa`N [C̵RTxPuq:ݶsko>xì?' SWEut 3DyQW[#/hioWc}[/m/Fygu+ &֟.z˳@yʹss霝g=]\ZyQ8Uq%ke'&[ucs>Y:*煋۴I6cۻˆ&"emApOv;~+6k}\?ETsѮЭԾ ̋v@h;-xe2x]xkw0;I6MA[6)ñnsaGhC?Wki%7-+X1}kKko{!V;{ p6d-ʅlmƅ{z_' o10є\+'7 oAܐB+k 9VT1$] 6Km|ԁnzlgQ|?O{z5ICЪX9w"@\DyxGQ. MU؞+nf8dGΟ$HN?W"mP›Qxj:˝b, U[}Yp!_ou;idB0;W;xĀh"p`@6|@\vr',BuG:y+iwci3Xendg<0uO;?3rb];LN޺Qp,vo -o)yP\tL=EoSpE#0ƮI¼qvL\XTq 1#^S̝% et/r)";N(0ucl8[ͪCR˗م6f"U=*^¨qHCy i<\vغlexM}t?dW{p\ c]10zm e\u9\ӛu`jZ~ڌ0u↩TlQ'0G{ܶDz_ <3?$*GwbU4t ۼǪplY+^ڽ[.ڣw<Ž߃1bDa #"@\DIa2ߑuTkݦzEzPuGɱmƿCس)ErT ) e{1,}S(W .P!/\/U0b62fm w/`j,_ܯ7͟½@0# )[iNlS8z䋯K,w\eeH(%/ȋcJ#S{H) G˼|3GexehD2Sr5{D|^ ݐj؜ ԁlY616mJAߺ O~^IVTn ܉m˃쭋xƭ]͊i @^w<7;R!{Au%p}]=ym5XυxJe8N]6SqAUWx^ͪiRRϸ &PIo G޷c9Xt&LOsCi0`U;4ꜷ^Blx9<Ih ;Rx E]Kz.zi@`STqm(FV,z I5%r l= (0SE4cp06Lukp3pRЭ;<;jcM.5aPr죮әQR1LJW .C|RpWEtx1"`"$3#?ݍQЭ;e6;o¨#ie7uTVON6*^D_EYϻvcN0?kQjmN$y@\\@\oj\us3IjHzo4$L,'Qe@lfTf~&ʀO|?RՁ{K#Usqq;nŪ4w3 tb`~o]zn+{lM("o<2@1O\Muux+9&hg[dGǭ)FF6g<| _ hu;V5p'Yy.a7΃7G]UxU,r"U E/v0Sx7&103 c>Ʒ|X1=z 2[.*1&hCf, eS8OqiS2mz( =ex'>XҟvU;|.G,vș,w|ە.˶%r}iGKspA]c?I3Un8D"9q.|Ps%o9`3w5$4w_ƄSؓ{($lQO~K|_/Bl1z֟n5@wnʆ*>R[$#7ܳGzEpvT2 ч,>zwNlA[}{ |@<` oMzrze8j DG*II3\EM6]󊣶لphu:lCա"gsixPE)w\Kiԙ{ ɮ(& b 婮^x^ByaܧhHθM [4" 5؂M{S0Ʋ<;jFzNj#_x҂y(/s7U0-۔2mop~]JA_N9B|·T5{0g^x^ɶwԳ}Wy1_,[a Sq~ =P:J@wŁ;j|燧Q m X0UX+\$eRpuUBћuF#<2`{-hû+ssMAΪWUu5t[ЩY+煯_f(0n{E8a7WwmJ#1hb4t'*Э;㫷zj;ϲ'wx)IfexcB]Zuk.7^pgy&q#,^-fa*zE|+O~ GaTiYS;jk^ 5xy0I%Wθ^Vծ'~%m}߫~|a, ݶ NޅUTAL|U f Iu䧇lQK,l9e/B"c]ן>#@*ojNL{-+ ʎnE* N ݔo?ﲎ0 TU*j\yL6o'0cަp $o\뷁a郮^q1Kf YkOr%+VS'c<Y '3y^Tc[ïn<;b:l B`Z²a7ʎ6 Ƿy3 \'m']kZZ2DGgBQ1ڎy.a–m9jÂmZTuxA\\=)Ws&z[hh0vZCexPW-i/ewܲXxQv6)mzR-X[*YVr=E{WNFˬ\xKqӀQ! O*E$0b Jݼ{!aĢ{^+EVǂmGힴ$"5JF3q֎^ 㛷nJMi薻L8^xU[-f#ַ)wܯ$P^ds Z*c2ixQNe!n7}y1/ƃ7:;v,Dȓ:?)سOQ|^x؝NC` ٢ڿI2dI7w K<a ]7C`tkVb,UѵuJ%ozo_yVP/*[ki/Grl@%r" eiOm0HUY(g=!R+/|sE'n<\S,&5d߉wy pa HCe3E%XkyÔHk]`-ŠZs@0o{>{݆fSj[xPKg,fjoni1Y1!![l:Jr:g@ȻU2^):_^K|:[ys:X_O&ȅ3it{uQvhmrCSvMG0ǜ]vw>4xa^]_98:ӄ.`dt"|F!(a(~Yd 5:竫<m/g˪N)挻,mAY]99VT~~0?c}yV`^Lݞ;>ٲ!k CN.uTYHg0%fnP'/<mJsBȂ!z7m^ ;\|iN p(ӡBjb7ixng_nÂ=0.\ aVו]f^qWTG 0愩@#sC%٢& Smx^Fuu`t/ IUdliQxy掷|Ïe"Ke^;wst[)렪{ūuA a;Jv4 E4c~:g[lazz@!tYǙÖMΧp~rsROD~8@qA9*{?ݿ R}Ga$b ~DŽnhW ^ &/c֣vCrƿ  Hu@dbb%LDi~@8AVd,ʌ7[W?f䭮7/ؕui-9ǁ>~p=t]0g;.gG!yq|xTux>>@0`xᆲ}A 0G5v[k%UսUӛ-,:F3<5+#d-ݚ.'Ki2;(k6}}j.pr4l1_Y:oGUڵ+q{P>(xPsb,̇<s{o%?zEu@+Eg qMޚwJ Sˎhzz}hT ttdۅ YRƗT#EE@>>>[X+ulw O0xT0AW?0>(¾>Ȯ_};kԄ[] gSսUq @:*yQv64g;lTg#fZ?(pu*Нl<]Oof+ס6p2*6 c~C qɵ  s#W O#>OoV~^x((%JOkX+g6ә!z,&Z*gΎKR+0KLwau&\?ڎhVj e(D pU7 -ݏW&E 78OML5U+A<&k0.//\&/Qϸ dT;HTi2_pC d.Н|Y< Ɠa}av^}V'ÛZDy.c~aH}V8$x/wN 80n{ūZY4yJg\shP801~aQ(K*zt}5{3 ܅-SiΎǸBEV?BJPM$mS,{y Qe!i䂸1y>sL%#2 ^ixMU%/<BɵQiӨL8*l2;0>:AiT3 rUaGpjUDz(Sw%m p.{XP)ixuwGVYf^xl?64ij|r~:yսĵօsj@x\_Fx.񣹅@^:Nm/&pt&~][]2=ˎKMyYZN+1fH(^q961Qn҂K#(M^xު39~ǂ #G+VW0P^xx_lQNigɴ`7?{okIyխ[յWW/͞E["mKI6`C^  E ֓ ,ÀiZHGi(ңi 3^vrLgXȌȓ_v̈-c?W|ʎO ߼.ydn*?qy`vx݊VRFimM4L,&ai7&˕bhǮi}$;n\4k (N+gG]\ik"n#=b{8Fg  xO1llpZN~!~?g;9ܼkBn*} Et<<耶sXQבeD|7k6IKDe?o.0 Q`AjDeqK$|%<'0Bƣq '\vyԫ 8>ZIr~#N1NHeSlskDThs_b]Ⱦ>x2FμpL[ Hw"C]2Njj^8/O{!"5av_uUxއW  z-Lr'<̲g\,g]Vԗ oj|gN% IDATͯ%\l貏b8-Լ 3KBpzCoתw+2vyp  2ppu6x}^DÝ,O _VM#CʨxJj72hnk…DūYb'm3/gD|HmF½>=uxtx IA '2q#[ab%uf7>yt@w2_9-J;x~Jg2N&] DW񪷤)lZWPOp`]Β} A@dD|!6#KgV!`1zԅGis : SmE>3U謤·}^OEiIqPgsYu]\}nݢv:>(*&.EoJ=F>Qd6Bćt#yG0k[B>upQvdެy|[K՜ QCleVDx߼Ip/e-J;<8NkqݧrE\tE"InD7E\'ʭhm3 B_͈xT" E}d>$[xZ=I\o$30Cƃ`m|Kpŝ#jd]Mpvr CUX>47tOQJP̑]tmEõHěK؂+3e*>r"~5C٘#BZbO2B~TAȻJĻGEXnopZ$MΩ&{sW/G=Gty,\O½LrǗn;D%[vLEO%n:.5"DTtԵaMޣ$ٟdW{ťy(wnǣ=jTDj{"~%æ4g<^!۟DŽ;I ؛-M! /ΓY ?{­Vg15D]IbCvzKSjeuZ%oSD\MuADk::Fr(Wsq4'ьըxWxQUd{o+:pQd< k`𣹛sWX8np6g?xܾ^S݂/NExƬczKX u$|Ka:Detle-3"zJYd/.JĻGe@oDHR%QK tWR>oBf(wiVDi낞.xzeuU֣ᜓMq E`W6 PkAñ0yb4INE::~Z.6 iFd

  • Z-+FD)N~W $ .WZePW}0#pMa^H;Obb݌/ErW$ãVw\( zx+&Yeʖ/߄_lȠy[@ZXq>O>upVFL@xs8ݻDPet!M:jڑq";Ҳg>cxՍIIY{!ݸU9זvLW::jU.5Iaʎ u_lQq1H+QFķ DIxU{ > ٭;t=*.k64!᫓ۚB ߟӛF+|O?~/ܾQGHp/JyuM_ )KFv]*} dt{߬&|qFS:lڷqKl>?r򽐎2B)]͡Q:^MlDWyጭ+E[0P!Q{A THy'­+)kOf[U'{i?L |.՞Wp#:5uAܺjg QFx9ØG;:/\&_! >af`N*:jM7#ha^@. DƯ\SxGQq*G}!EM =VMp/T:'3TdZ e´]feuH|.dhMwJ^&0y>vdOaQN]t=*.'t#Z5"QmC(<& 3o8=LGчXPٓn~ߤi<<3}wY&Ykt] mEMs)Rmʎ =}T1ZdhĠ8J*K!9ބwSA{ˡQq^" SZW+ێEw=݇G`xq۬=Mt9O7vT&Zh2F4\6DܔW"M&3 WBV$鑀ۼpQv8ޞ$!OTkdwuWxסVSijbz^8kF%;Y/^q$ EIO<{nL[9MGUݟc8;eF+Kd׍2Bw17Yg"6 CVK'_v96/ N٥y!?8@e3WmpټN]!umBUT+񻤁 $SL`Nnm̎CVr+2ļp,_-'2]ID,2A]#4>9\-\ƞpʡltg1dS_sPSt>}ovPw/#a8$|)ܼrzكaN N E.FŻNmyE" ej~yJ~d]t4O`dm!Cҋ z͗CS"<=/_[6 [xr:8I9D'7t՘ҿW&7 g6aKl KxBC}"O\!!߇c8ըx׉mEnfH8-?+XMvyA8w —op}o^+6 N<Lnuk d_`Il>~6AAnҎ 4ocb5 b ~^@N^r Waރ);l.JĻZտodFsdInqvyna^qe\x|89 lm}'yj:וtq"Vfd̻Kv-S׶M;=oGO4AfhQ硟.1K‹Gdl£N)Tm}փQqy5>pV GN$lF=؅?J\r/ "M`X|O<_A>@j;_z\Y7m[w <|!}۰NIwEᎎgdu[FYbs$V^ȳI]MqeWc7 .l]6?.ѬrS$z !&p\}"!'dz /`X^ۆr^?Na?e6ߣn~q8n4&Z#2gKf"yܩ8mmp$::z-Ht^;ĉuۮ;D'E}k$V^Q.q&De˲D(o '\.pʮƐtT_١ ?8vP[rQx%1YKRpS$;0LC~r=*&wūfM IgΆWC EƣpiLnqTimB#΍Ξpx*I\JJzKwٹ㌜ۈE".D\ێtnb y!NpJ 'PC1:$xF0Qwc_'>|'nB|vklo&#Jy|qZ ósEVP/dz g9N`gsDOtG1OU%XCMtR'ÕGM3]jjԉdhO<OL?/\P;-!b˃`oɁ^i}giۛaVş$ 5L$M?vDf|'4Ht!=֐ӳ*<: 4VvQ~/*:S.$\E'vt5Yl]Aφ#Lld$SrH8"#Ce4W!;d/7=N{_S:M0xۛ:dݰ^Em!t_Q|OrHhuw y6$\6OmSӔX(pke􆚛l-!d'XEh09To ߉P+_s1Mgp=(; _Y<L9"h xJ~ _UPnDH' lpޜ;{͎Ut\d.pml2j=m=x]=Xr DxWH8S1fZ\c gP!#MYz.66ڬݗvFq5ɷ>98{gnt+Iupc掏d\ 2^u[OG=IxGFud)b5{MqY$!܏Ɨtyᩌ\!F!.;Tn=l|S=*&ϑyc2:;/}t'irLv-ܝw)ӄO:uS\KDu[OG]لѺeBFEao~Oqӝ(Š鵇riM9LQ[zQ|-#9jpi&B]bN݄ެ$|{VS` >p"UJ'=TN釛cz_f6/fԼI6z]#M딉A._Fr e$>)q-Q|Op?܂q;/{IdN=_Ifd< D_\Eb\jk0 )n<'rbӅĂCdu{UxHj^mu;7)=AV{4X g2zd-ռp$|)LL`x|]=/7 ۛiDu{EƓ4j ՝X^4>98t9I}۔&7fh UZ\'tԃ8:VOH L2f@$Q#ypQ +\ FVt#'gq7SHtF _lYd< Yխ!mD𥡪 ܿy0~GMltRg q@MAȮ o.xueo5F "ޢ-mb`xx=uSUҋ⋢G-2[2B~ ^}ngj+X}7gjT!`Po9q)pm#GMtpc'~8˜I ho߈xl DS4AVLPX1W#>)q-8aJsߣYrĶ= jIFƻLdq1ܼrkst "cRz][Jo* # #AVLub^$V,GL@O\jH8cxnz40wx] 7dt}ՓwC j32N#y{jyO0=q+>U&R$%EJӉ!abK\)W( ->\"`ujW>y7Ȫ귡[&N>摅)]|DSI顔 IDATQ4qU%L-CEH4\CTh D9*W_ė7p/G.| ۛ]K0u =Na5Lڼ=u2sQaqN4+sݣܪCԫ9Р;ޜf@i+cܛmJD\0km DbK DXMGbyHgX'8h7jnn}'W-͚yd\n ǧpF\ne_< ' _\__OK7UWm&lF'@OěSL |Aac?/\uJƳCetGEgms]%,"ۋ}c_*D],NlDWk5L/'ds^1SPiG^ZO5=V $zNbÏK g,7=ؗՇhu] T^8Ѥn¼pH8fDrP 6, AaNӌð+ ֔CՃl|'<<=­35ne.nLiLEV\VdHOY#s8c7iM"nϞM'h /WKv> g}*ҌP L/eu~#&x穽e*7y Uq<_adY2[ }:tȸLĝ"qgr] B}> 0} f|nn>j|$[3>Q y@n7N`6a4Pܓn_u " qjpoܲCL`KLI]=W/Զi*4R/j;H8uJ&aDrfQ)&|{$|C9qUDIWPյN{z4{{8i=VY2[Iݹ`}ZMDԻ{՛ ulB>pw1-p|j^$gu4BYg7a8#Q D/ۛ] /&-@[I$o[7j ܹ[GutgOv痲emIZO ==fTi#[4\6[>jF j7 [煳ʝ ԉ. '(d/Y! "ۛsEۛ!8\x='nE o+&9H7E E~>7:\_]oqRFmu9' 4ȇڼpH8_cǢ"87/0 /{m(5Ikb7v^$:NȸQ2%;} 0gעS]Ew 8G'!KC=6Iv%݌VR1[&sh*m umDU{Iet^,j g嚌֋@@H8%T绕/>Hv^=\dVlh82}˕ j(Dɺ}*>|g9LjT w{nzJc&Rzz/τwQ5w|u%:aFi +Svf Wz8?9 ߘ=3{wYūխ/&[O6MRudVSImطwe0*.2yc>+5_xSc#4oBV^mDkK%hړ[NnE#N=r tcy ElCc{Qq.`QB[^{Ed/.o+ef`^׳)I5?q *pltVjLS_ YBdu/^QVG kM[UU,9mG"N+#i"\KO&l[ $xA _'R./Y%?_)[u 꿴D0׆t {%hz"'L*)mgtÄ.^lǥ˾9B1)GyrhjxE v9kF%o&<#nʏ&lUo:l,q&A^ /9%NR`Tu:jF>d럵U%tTP@~G='Ʒ۳g8 e9dKŭ'Ŵ t\;(M"V4Nlj8ODcx!i_<|.p樟.s3vلL|gnjı[>q8dp:_a0uۺGoMO22_hrTIh*YkTh5+~)r xue+Zm;pI8Noû82W](%ZQ_I81lstN>?>Qދ/'+nsszWɚG4 |a(^6J+/ B͒p"IrUQ $-94G]m4sgr0;HD#e럵U̥Y/BR:&_M"gR ~y\Q 4Z4x:B ,I OOgTj.$殓+R |wȴ W 7z8Iy.p̖(>RVG- f5pc^z0?5 t!LJ_.7~Wdڻ*]{2>7Ȅց S7.D<#7w6YVJBSMnY= `i^7Cz"f䜎۲+ WkoJ!j~6eϑz$Ӽp$t`NEy?9(q[qu?޳2~݃= ڞ_^y v&k}AO|TnNO P'w0{l'z uu 勃 M~^Ԗ$W>pYQ܅?8OSW2eFޒqI @<>0+H?Sɉ8oFE5ud{% zINBgd%u|`5^ߔ-9AdUyvJʨUä}NYM͌H6ؖ$ث_<*IcKhxPlQ?rPE젶#i%ݔb|CPE2B^rJ'r ~dr]$^\|{xn]u_qVS'U*RyT?>ladv CSY-yzij}Q.]7(S(FTby"F=u\~-YQ俍!#Mr}[ >@=S!2Jo7狛oH9e掋d]QW%m$\v"ܶlFAy: wS'y6^o\}%> ]"E=~+p)-Be:k* ޔ-HDñ|{ebuj wU }d R"0u#6} q[N[UdE?/\`K'E ᔘȳVD@ m¯E4"w~}u=ϘsU+HoCp{[I'90np bIf<9ّq45#ub䜐UqMFUemFU !d8F>u~^-H8eWlb}ZœK{$p|_?sD9Ʒ~J;!U$*0u#[|Tוx}<_G37zIha/JmD%}ˡ5q E57}O@Mqw@.g@gwj^[$~!;^n]qv[2!زh+X'}XJ`~?(GC[ši^||+*n=z>܊6AXj!*Yz?MoUDqa!EGY3ggp{{B*NIJgguR!E=~0Q[*FePH5[EMjNr:/ƗAmE0.BSC}Zsh^\3!=OO!m`ipUNEyYh/-煫@4;'2t ojJyv>ZsDt/zibKZ ᅑqFxo|w|Ji؄0Rɝ_$P<~ $W>pꈺ=؂?0^.|lT0nf$|!Ljc:G x/3 $gV,G 6b S3J|^Hs}% @} Imב3SCD\!8-m_9=T\Y6] pRorګ |1$0+e+f/é}F(/'rVcgR|{ |a| 4P{}EgK(޳{ fMQ2~oX _McNX6a\zNDٜ5'K'\(im&1(>&ބ:] +E*Y^tݽh/7WgɝIXsI-^F#(!\y~]SQ 4U\Q%2trRb6Mn#η[D6"6]no6S&B"|yĭaM?=gd|]U-_azg$.]7msfGkXN˜GN0 Bxiqw ٜ΋1v*HH)ڋ2π}Cb*Ψ~2SZT|cJ#|ݸΦJ(js6Eg+c0(FNz9= лz/+mMj 2[@GΛXW;F |F[\".am*%hiَr]6I-ßH,^H/Z/n}2[ڃE!2^_!8uDP^:al765z­]*e.`ɮe6t(Budו܈Fp0zKVYr G1𥜽Htb }"h7#KG3/ۚXں(7_+|t.[C)"],P1`Hv]*=yS:l7Mi`cj~Q(?N`7H\G=" Ht @¹~\툒ʰ^jnF1R2pǢ)oO9,Xڈ=Fޓqм[M_`aٜײH^vN1.~!,eM2 _l6ul e8 *m |p/8_8+suFgq0>NlTƲ-Hone\*y{ŵԋ.dWq Yd.XE_lF|g.կ,\J{ዳ9~k3.8}Ap5ze|!^vNע܈RavldBLkIRH_:$t15F}Gmwc]g lIMHJFY[\JG ee` I[ Htd#gbqp;g}xY[v.O1$ 0a섡Gƭk-V=}ھ$l|@7ˍJa1T_,EedeCWj(71 IDATc7ct0B'Yq#1u)$]e9ڟ@Q/_}YQ/Q[/K|~_.ެX!SHYDq[ KYI[ǪkÌޘL*V'jK]rbllTd\Û]7dNh'~oC%PJo56Z; 98P;-!EB@O^rՇ G2ݺ>UV-Fp`S!Lu kG=ʿXdx6ғnU<dZdd2@]BBj˯ ʻr"N?$\$Pw)> g2*ʘ%ZCҝ >gy᪫zlC臨[jh[Ͱ5;0EE&%4r2> X8ml)%cER]/Z [O5:#\$cy &݊} lɒ'8kxA(]/Np[jk^ |.L|K.+3DC _88^c[#^GXOt$jW/wu AwysɮX:jtTQq4.աkWhOPՎnYL&F[9_~UXs]eҀnɅp}e.flQY~?(2(*AӘ\5$Z5.}ޗ2"Pλ/ o|+y Xn:߶ d8KT:\"r1MuG6Nȿ^lNׂduF@5 o'z;S: Mhq "NioI<#SM—WFN' z-NRNm+~(no=wi&ד*VfS:s=_|e0fs6ⶽ ܸ6Hbh6՗vLnÅ%Vtَp 7#j#~-Fْ%O|qn$'yTVGH[QpF_8 _=~>[Tka,WT&~z%~5AE]1^˜1 }˯7\*~8]r:~TGgలZzqCYnYCe^M Tmcٲf#M ˬpVw.V yJ8iL]Cсyd>^9x^ܗT6ًv-}u>}^P+#RH׳dFz#JSg"gq0~"\!u!:0I va>,} "5iJчpC~vCuld_YzC ҁHrT٣ze b|~>t' }q.{_%TN# 'AFm2\F#ͣqHy_n \nj{ypK[&Hj7.,8E:z.,FWk­pԖ&yԋ$ #'|ո>F^GTSC&l܂pU}?ϓE7$ԓq$MH8" F'rs5ŀ69K=.OlÅ5YQ+ 'j/6hFQ}S4 [F_[_/`wun/r׆AG;wXt' .߸3i_ٽ9ݔ@zh>b]˭ yEG˕V'ȮU3Q.uK&A]]?Udǁ Iuq޼py|}%un$αUWg7܁*yp<]\U"ŊFyd;&~[୦7 ZlbU>mVJ,N…fT\RO=/}]k*[וQSɻ} YshfAчp:L{?/\?uKGpn>z(o"[k߅x&jn¼qA$M:~45k:Ke8dM% 1LB O6 0%Xq#o*$o#l! WOwc4I8P;-M$> t#l$&Glp}"ÿ9|8 W`~ʩdƹ㞌#~ i Cm7phIHMEegqHm5xQ|&utI!:*7(߮l~w֋!O|q'H8kK< e$AH8bqyFgv_9zޟ ف/퇣y_W;Aƭ'ᳬڄ-_Pߨ@J}!m/`^8':,!$ KzD'iCVM Tc&hDy^8+suԼp<[TZZN)})A\y$Sv\tO0p$r}{B{QFY[\ϊP})PŤUDd|A>tvQތ&"nNZ}nNS\F)C`p7_>nL<nhdKvձchO ,8%R;.MkIx\U%ssGIOC G-pf}uzsv׎޷ .Nx e֌12N8GMٔBwyM]^ZM:r$Z }H @٣z4ɓPI2 I>pܷuzur6!7ْ />Ygש ~% _jf*86跪?<% wEL'B<\ ؤj7-?^2~#z V$/Nْ%O|qn$P^ *?Eի;fUF;箿q=8Mt';"AH/Ӥ3dVlҞ'\& ,D\wA6|TgI^RLFƟV:X nxs:zx?>pC]m>T j=gF芈<}D٘rXT$ÿz9x͑J\5$Zhb9gcd=ug㕃{_ԋ@y[&=Xbo ~uQ>$Hd5$WiGƵ{d&q{0xQW>wp=]--ZeQqݍV^& ړ NɨEe}&ĽH,įR\E!2l TVmd럵U̥O,xQY>狣^?h?C߅SD.Rs\Q "O Y4r͛_iҦix昨߼)m'DPuF2۸I>DZeĥvQ|BBz"בm_bt*dk&ާF)΍smQG2~6K҆Q}Õ#d(FbE]/W +#Yz/hi]5 # %w}OՊ8ZC^K So42NUf_oڮlo Tu,z=Ib)ʭL0Óצq/Em^ڷOxXy#E鲧Y:h:N@f{4؀pټgؒ]X7WoK^#y5_|7OZ}h*|bgE_ ]fϳbNKdDZ?L]['3$X{[%# ['Z'~B8$ eLߤ,-$Z8AXCeB@5;WqaTK!oB_po0η.kae# vI8O#Շ]/ Q  ᬜ /&M'犯yE{mvo s}$Ag6kt:/ 87T]uSy\|s<4iah0wEs#:PUʕ YKH঄jN29w ~0mL^mÃ!gp\璫xdh(v١݀I)>:MP^zaeD9*G~M :PIRLŤ) I6[᧮D}wχ(V ~ A&_k6@m>0s IߓXmiM5$LVM\$!Gm&d].07CoB;1܎˶R!|:A XP;-HAMDL(Îdʁ6[CP^|H ҡJc=/[{2nt`o|݋*abc2.b_Gm_!\t}i-瓚eezdba] ,^&qޖt ukZNGӑrL&s+/x:Gq.0Kseʠmµpq'$Pw/KyH8%4I ~p.`?/܌??SWEޥ8۷~|R.(~ Y-'>mM9N*-~Qe3q) (åhx?"t5- ^tܝ؜&Cy^$+6h>$-grE\b%O$|7rB~@ '(ooVɐ:'hGAqNƽ'߹npGiB5rrsDIV &܅z1 Ghrl1, tcڵH8u'(]/Lp̖WSe ԋ@ $|)'0WVoo>:a]o|㷳 z[sWk7.1Y*p&BnOX̾\ "?RtIIE\*t=qc:z܎Vqۅ;c }˚ #xc |+:{2Wg<|Nˉ4ţe[7J8~R|*]jl^tp=aEtr2O\l2/lTFW) 輫(JZ {VpmKFXnovf᫊)_NvۧX>xh8s5*}!D3VҹH-cD{o_z`CȇuaP Y(+ !7۹H,e! "LoXqW^Jr^`M%s]A g,_ ?sS·v8?{CD@g5#h'0^s2t7{u^Si {$BJ 0)I#Žŗz/.+QoWjDZ& ' ~ðp$ɕy6ZEƟ};1y)ۤF(L{2n 6~%V[%'odÇiL5 "Its-CpsuBB o2^tuN0Bxq@* g g슳!'P+e>&煻@C}$;>-g q>7i?L($'&J׸_|'gh8JUטpc1 fh%\Yvsy?_;ʡ!Mzg0 ]hj@IKeɓP$Շ69Q:&G gj?g*8 CÊal܀pw`bij%uv+2n+gӆ ~5F:CxҋQ83B^\u-U%j-VG'*AQ0M:N rߝhuOK oHsN+K hfd|EıW(B}t(.5K |-p\Xme/ErŅh}{kc%9Q38·|1 ?_ew|]\t(/I5oQ Sz\v*2:_"rV\7Eϟבp>Q-g`V?dt!W0rTLMXD0Z';'^ͪ%o}0KGM\:L%( 8p>5چqx nÖخC¹hPVSn<dɎ,yb$ɓ%O/Ή EN9{\>h8$c@rpKn],/KjUC*&KQ{o [7$PC ԗ0P8'㦑ʌC*_"M.'3EWX5<ݓDerGtR'cT\(O7լT=LE"lu wf^W(;pY݌# >$K0HdGɒܩC!bDFhʡ}_ |1iyVuH8PSN#te_7Ȳ@"I8_lo&˼M,Oy@\=a}Jy\ +yL-4vRt}ޔaQѿuC7 ==f`֒wPYPS\W~7C!${n.s--Q%HxctȒSd@I螸:CyR;Lp.A^rz)}Ŵ5yVܳ"bNe,&2d/?3 9.qx ?5nyJWpR#¨zIڂΚeH`@sVx]nn^2L:<⤭xHtytJhxy{廷B*bEI61)e럵U̥Y/BK#,ɫ>FNL^$gbqPDBUw0tcчpa '|.~՟GV8.󳊋L[N Swgoeoײ?ۖ#l"f,+רۺn 1 >_\"zR2KɨwA>sP[YT۽n&oHH8uJ&ay⋳v筒IJ>ڄ_`[πs$2N IDATUvGcvP;$/pqwse^ɸ-d?hd{ՑQ44EE:t'm.7w%pCJLt|3 [@ְ"H_NA!k sBT/H8+gsBI" g2*C'bSHܹ[|`F-sy~,;{cdKϦͷ~ӺT@|M\'~'ד"@ii2+ /}`.tcNFݪ"i6 G >~I6qJ bn@¹~\%wzC%IY}p:3>Q2AY(,ze&lDo[p,hⰸYJTT)2NMhhf@iS.nD_ d XQzp1Z]oDWq[]7[`7Ep8vP[I# gy|e^B[Z ›&Ńs>xqN;$^($"vj6'VS/޵o)X޹u3ARO]'B˜SIrzWJ'b*Q1cpsy}Qg'y(J9_)Dh o]qs '/#+Sn'gr;Qgn̊ߒ'?ťd̯(ˀP/r8|)׿W*T¿6s Ptӕ IW8 {BN%d6MU;36CʍQ+ ǕJhd4r/P);p#\S.7Dօ!c$fm^3{0'x~FSQ"O$y!; 棭0חVNL8 uTտ(](ݙ q_Rj?_>z)Òp * 58%#cH>u_!\?w'[׽aΑqíئ%oq9ߊ / dSAv; kŀ- VW IegU2 S6d1ZzI亞 e@ћs:;^#DYd A3U4Q#nh/s>)q-VF$%hH}>d럵U%tTP@Xr#*dO~VJ v:A(M=_|Wa0}sჃ=H#$1ɛpGqZ9% zww|sD0Ncl+@(C' 7%iʌJ0w>=&ʴ.Dׅ]deoHȎ$P^2 gշOtIt9j^H8S_ ;$A"[t]s/ x#+G257y:$etsIX#^y677|8.tA4w|5B^nK" [\73/G. Ul>q9X/x.p}`(͡_* 4F~C _O `۸+Ȋ,G7g'$Ću^|MGה:JI]ӥ RX[Ӊ-0+r]6IM1[&s 88B}NW CY$E<1  Ssq}ǿʮeRpo k~.<;6\ǢP.}3 4s)Z]S_ڄÅ:66GC"8Νw.P$V>Kf/IE0O@P@([.'U/Bde@W:ub|;p򶞯;=!'.? 2p:9hct(Oң7k\P[:GƵcG{6k|4YYnF ,s) "D &D_qh5.ӑIυ29/w;rbyf9GAބ4 2>x7WD>'@IL Nt#!. 4@M;'H8u$NĮgwI8]rp߂?:Hl|#Ydzk7&&70}םIiE@r `O[3>K?ܼZ[M}Ի96:HxN-w2BvS>$a/#A_#T>KRw V G!K*'1P/CRc O͓*(}rDW?\ l9@W^ބgYAYLސ_88W6~9?o^_{|7 8x <%"~W׊ QgH:y+"G23(,*\.G; DV r.xxK:WFEAdSltH$OOl&hTa|Z8*ד$׆_s[J'.INC2" B tpcѢf'!=e:!2 t~T #f~B\%Ae$~Dz$|Ŷ6 "I $9BȵVD@ m/p-r "yLtNݑp`^$Cka^8ʿf4۰hx/|{|م٩mr:z4`7AOF1}ٙ`yHY9"H*&07l_aGÜw.)Mi1O.H(({m1Q!,6.Fbb }"ᬜ r g: W$x|q79lw^?$\ſ[{W^q?p8+ BHȸ "R$)&#&exqyǪq q--B4>e2>Z-+uvv_I8;ЛzpCٛ%'+ߛ+G~날V8%et~px#wvI/p5N+E煛c'GylW~{$2^pjOOn^qrs23 ^Kno[: -"N#kHU G5_ܴ.x\ !ANĻIpX\tQ7Gr*Oo\x}2NZE'YZ|vn+$#grE$i煻@k7 #ٗ~~냷?13z:i?L)䋹[W]~pםw|st'vf슐n ј _Rs} dץ29p5:dP% : oxzyL;KE|?b?釟~yk,පyu.ԝ/<;W~>P59𲼚>J|: = ᎺT?S_ڽxW9UwK8#8JfRdG©p3B>V̳wsiŗ( 9\=W<x"$gQݦ71xLO}i?E!PK~p ׁ]jwx}l Z+y}9םte'p9Da+Ρ  X& ,#=!E>{QNo"4)ܽ<㠏/KOJWw O䛇 KMWksQPQFHF5$vwr]r{uv:uօk(hhqڤ_ !,?0FQ-i\Q,;*Y,K%Y}}޻K\r;_Yr^.w>u_C&So:Aupsa^|AES #7j-FC~^0XP0.KͿ[ M46 ]bcQ="mS갥ES%]9'lk~جtk6>\Š<}Ix}O<; ‡ʩyd ,Gk@}*O@"P2eƿ}9n>qخh>oksur|Ʒʡ1*|K']e-(tN!_;<=Y+׼l{Vp2_} 9}{}y`5/<@3.W?^|n6뉠Uz3(0գuϣDi_ڎY|al@œ^^@`ly=t%{= 1v+;{|8`c0T0+ɐt |Tz^.O,-}kWDw zˠ疚rn}q3ywHw9E.ͭa\xk")ik" |f`{*W|b!]A=_/^ c`xAeCxY0'~-5/@t @P  }닟y;uC am|qP`Ԣ1yepmg˰n&qslB 3nǹUyҢ)J偀UY$tBÆâu"_y @Ba{nR{lH>Q >?K`g7/#6ڰY}4M)ϸ,h*!؊S11|Q>dpk }{m6SG&kOڎ+RW>)BIAxTxzWX5D.no'u96?tyBViE`;HWG 2_Y!\y'|o}ލ&Z,TyWye7W6[pGUo6dw ҩlwqǂXev'q%^^Ae:+=ͪW\D[r/P5CX|z+̃wȎ?/\"n_po-ޏ FgcEη7+f*L]:= 5E*_n&6t2g\@/ܦ`w55@yay|ӓ"WͲ+'Ƿ6yz:Βhzx B{5œ"&U827ռ|3m{}O~Zh{{+cUho] vM * # 5o{{j^ODoȷPEop$hekoM\JOp]脁Qvk0%S=/>|q!k5-3tzO*nEHk0\_L'-VzXoA|ihߋKoO6V| {ƹ9G˪o4ZVs([!{=X]J]&Bn39+F!f)p IW-Hj]J력 jtݺUkrK E;mXz=SKg-o{3֬oye7 ;M>b]6g.Z~=w [&\2~-crs>:| A ch~[pLyӭ5 We:|mTr(GUFXbxB( hO]ys-lOk_DԠ81*[䏳t⢶;pYNmت4%x2 Q?1/-ZK 3 ɔd:e@\nDXO֗\8֮SK77@nX9K)=i1 bS06'3KI* K^{aqA{p+16=Mc^le_4nWFտknatb=/+iW US0jnt4Ĩr)0>stJ6pW-6%G9KeǔWEߙT8HyGZW >skI( E%ByM=O =0/|GƠ\u]d@~uiLF?L/TǘdI~=  ߾6 w!A`SA K+Ix v_t'glF,ʦBg/#d$*;yug1 jU*=;kDC}wݮc:~;|/os>Ku(EjA _a,a/dKy#aS y/]횣 N9Q~dk QE؊GW3|YiHVM+illi닇2 6%h_rEoz8o?h~ Xt[xWw9LvlG'(|ܲIwMVABv+:ǷOEz,qO*A EdVf]+iW PSӇ%{lYv{cSBa{!,pZ :Xjd438'P/dþxC95:D/1ǚxvJϪOAgs˓DzRmૢ!\)e!R'gEUxl-%>&]SP\[,Tyt58ֱWjaĽm{t}ؐvtγ]{_Ϧ׃JM@v}2oЬ|PՎsҴ?=Y jx_t'zDY$p_v~+>;KI*^q!Zw|,r۱*ҊFӱ{Uviu;=P?9/`CǏ7SR0*uxρ<ǥ|t\'k̤ rALQ;qmSP.d a\xwugA5 T`0gFN5q^lFv:w2aE7ɓSHƥFM#b:~=:cكoM|9a%D+.,-*㓂,ރ 2\UxL-%,<&wMΈr/m"S3`zF^a[Z|t%_h>/@9jv)Վ(JcO1xCֳr7()ߙ!:"ZNLXCX{,^iya7Tn0g al8-U +W>ܗħ_RʪWܿɶbmV *6v߄NEu˺|y6jv+s3zFR!t\~}9?s|̀xl{xI\TX["Fݔq=ve_tΏmKdN a\xwugAa9Rpu5,]_4%^{ UA6"6]}aSwDIZ)#d4p_:onR~<]݋k\Xo`A[;~y9C .AKH*<}TtBQŰ-K3nAb(6(Ϫ3M66ΓZ:)tA)R:V~Pa;KIJo&aQuKT-tJ (]])5Z)u)c14= [;16G: ֖HĖM$lԯWZvMsxJIvh8_xWwE @Bn{apԆUzb%%`< ̒900˨>6wwDFg1\-W'ϣZ>yؒ s/ CT8(\UxL-%q&xyκaqO5+eK[Y~F%].}WͣVO:RU0j>߅9$Vt,uqfG|4n{1x=wTz͐E{e׮ⳳG%- tE#xQE-Eˆ F5Hϕ.R]4qMc>ڰ{zz}PRY Po6?ݡM*츗ֺ^{a`bnv> ֨vst- |qdݴ9[:p5p-_ GZW,DG&Pb q[,Kw4LݪF |5&Yס|V󨕔$%E[s^bwFӼxzŎfq+JE&kH+\ 4j;yකq]uHv >+nă"ND[q\g1:^.PYw\/3uW7҃ϬNCJJJJԦw-yFno;7v&;;_o@Ϻ}åf+u{4%Uz.G-⿳O{xEܒ /45- M}HƆL2f^9\|IC\.ys/ߺYz2~|<=]ؘ놽g_w 3_JwԚ]#136z8VW[ϭำۻ /W{<#'Bs6!;֥zA5-<0yžW}ypUac(mƞY 4Z[:p+-7~(ѰdorJܗ.=+mzOͼ{NXezWzdw`.0oŤ{3Wp1>І~X':h]@;|,ohLέ}/sƅ-bP4ێFKv~hEsDȻ[3<}R+b2kM`JXӪt')cFڊn&Kȣ0 GF'd~P1W#(՝7TQG-lJPVӾ~%ߑ~nv'ⶸ;n}LV-|5핿Hcƫie)줛۫>!`1I5> i*ܫ0z۞~ ZdO~}paNf;R!^2viX6yn ˞#֚1ϥ|$E6y7j!S~ygp@s? )7W7"_x|y;\SX8Ё*\vO:$nC]w*ChE.egފ>~z%h;GEܒYZY}o~mQ]eΰR^md\y| x'VtV̗ 움}-1CVT耷vzd"y "m LI6jANbOϿ~^J3|J䭝?bѦK}~wڊ텧=,s~ ʁy 7}vd9C9Á}!c{vv1{iQ$uL6fm}hxEYMﭮ"#BVcQOaM7 7gsRWϳdyy6=O'eaa'+uŴZ;*iݾlɹ`c&M{C28 !>._lo=;vutɐwR;d3.tiG$o/3M[S{S)ފhS꭬ )-e~&f22cS߮ KtˢM=OMColF x_ngC'ځ&X2:}/+ۡgҜo|GGqN{U>\#adYԚkfAJ+4Mb5`R T{K EU&ymqk"yv@˗^_7u%~Cre G*+ ǔ3W7 O6֮]Uu3/G#_N¯=dLHsg[=(to x4 ^.ZFI7ݖE1lhRGMiTJC2Gdc'6W}9iثn{q\)|2gɛc6 7l(t8v,?}-UnV(0H\[,/7(BrޮV7) 8S^{G=93vN2n{}[YwwDng4}>dS*|#-_|gd&Z;8GOm\Vl{v Cߨ/eRR]ȴJ&Y^yy .\?YB1_yzWΔ+VGNΓ{Ϗw2CXL3\Gpsk*sKY ~cǘL6F.5m?!/23 k\*|K)1:-AD[ԑTZz̃rr#dwv= 7!AiMrݾ%ja2ERLHf'{y[.m;TqI'eSEu5)֑QQU6cT_/8~@An"_S }@n SYdOu;P{iӯ7ۚm­cRZB&R榔i$1ƊjNJ59'olI2FSŗ {LY1u+EdXm"lKm{ZE}I&vr+:]m[yY亚@ ۆ KzR̙4z, G @Nzjug ?3D+߾9)yMS#ϑ2Lf8 åHש.>XkEhIENDB`ruby-magic-0.5.0/test/fixtures/shell.magic000066400000000000000000000005571412543176100205270ustar00rootroot000000000000000 string/wt #!\ /bin/sh POSIX shell script text executable !:mime text/x-shellscript 0 string/wt #!\ /bin/bash Bourne-Again shell script text executable !:mime text/x-shellscript 0 string/t #!/ a >2 string >\0 %s script text executable !:strength / 2 ruby-magic-0.5.0/test/helpers/000077500000000000000000000000001412543176100162005ustar00rootroot00000000000000ruby-magic-0.5.0/test/helpers/magic_test_helper.rb000066400000000000000000000021561412543176100222070ustar00rootroot00000000000000# frozen_string_literal: true module MagicTestHelpers def capture_stderr(children: false) require 'thread' mutex = Mutex.new if children require 'tempfile' captured_stderr = Tempfile.new('captured_stderr') mutex.synchronize do backup_stderr = $stderr.dup $stderr.reopen(captured_stderr) begin yield $stderr.rewind captured_stderr.read ensure captured_stderr.unlink $stderr.reopen(backup_stderr) end end else require 'stringio' captured_stderr = StringIO.new mutex.synchronize do backup_stderr = $stderr $stderr = captured_stderr begin yield captured_stderr.string ensure $stderr = backup_stderr end end end end def with_fixtures(fixtures: 'fixtures', &block) cwd = File.join('test', fixtures) Dir.chdir(cwd) do yield(Dir.pwd) end end def with_env(env, &blk) before = ENV.to_h.dup env.each { |k, v| ENV[k] = v } yield ensure ENV.replace(before) end end ruby-magic-0.5.0/test/test_constants.rb000066400000000000000000000050401412543176100201350ustar00rootroot00000000000000# frozen_string_literal: true require 'test/unit' require 'magic' class MagicConstantsTest < Test::Unit::TestCase def test_flags_constants_defiend [ :NONE, :DEBUG, :SYMLINK, :COMPRESS, :DEVICES, :MIME_TYPE, :CONTINUE, :CHECK, :PRESERVE_ATIME, :RAW, :ERROR, :MIME_ENCODING, :MIME, :APPLE, :EXTENSION, :COMPRESS_TRANSP, :NO_CHECK_COMPRESS, :NO_CHECK_TAR, :NO_CHECK_SOFT, :NO_CHECK_APPTYPE, :NO_CHECK_ELF, :NO_CHECK_TEXT, :NO_CHECK_CDF, :NO_CHECK_CSV, :NO_CHECK_TOKENS, :NO_CHECK_ENCODING, :NO_CHECK_JSON, :NO_CHECK_BUILTIN, :NO_CHECK_ASCII, :NO_CHECK_FORTRAN, :NO_CHECK_TROFF ].each do |i| assert_const_defined(Magic, i) end end def test_parameters_constants_defiend [ :PARAM_INDIR_MAX, :PARAM_NAME_MAX, :PARAM_ELF_PHNUM_MAX, :PARAM_ELF_SHNUM_MAX, :PARAM_ELF_NOTES_MAX, :PARAM_REGEX_MAX, :PARAM_BYTES_MAX ].each do |i| assert_const_defined(Magic, i) end end def test_MIME_flag_constant expected = Magic::MIME_TYPE | Magic::MIME_ENCODING assert_equal(Magic::MIME, expected) end def test_NO_CHECK_ASCII_flag_constant assert_equal(Magic::NO_CHECK_ASCII, Magic::NO_CHECK_TEXT) end def test_NO_CHECK_BUILTIN_flag_constat expected = Magic::NO_CHECK_COMPRESS | Magic::NO_CHECK_TAR | Magic::NO_CHECK_APPTYPE | Magic::NO_CHECK_ELF | Magic::NO_CHECK_TEXT | Magic::NO_CHECK_CSV | Magic::NO_CHECK_CDF | Magic::NO_CHECK_TOKENS | Magic::NO_CHECK_ENCODING | Magic::NO_CHECK_JSON assert_equal(Magic::NO_CHECK_BUILTIN, expected) end def test_PARAM_INDIR_MAX_parameter_constant assert_equal(Magic::PARAM_INDIR_MAX, 0) end def test_PARAM_NAME_MAX_parameter_constant assert_equal(Magic::PARAM_NAME_MAX, 1) end def test_PARAM_ELF_PHNUM_MAX_parameter_constant assert_equal(Magic::PARAM_ELF_PHNUM_MAX, 2) end def test_PARAM_ELF_SHNUM_MAX_parameter_constant assert_equal(Magic::PARAM_ELF_SHNUM_MAX, 3) end def test_PARAM_ELF_NOTES_MAX_parameter_constant assert_equal(Magic::PARAM_ELF_NOTES_MAX, 4) end def test_PARAM_REGEX_MAX_parameter_constant assert_equal(Magic::PARAM_REGEX_MAX, 5) end def test_PARAM_BYTES_MAX_parameter_constant assert_equal(Magic::PARAM_BYTES_MAX, 6) end end ruby-magic-0.5.0/test/test_magic.rb000066400000000000000000000512471412543176100172130ustar00rootroot00000000000000# frozen_string_literal: true require 'test/unit' require 'magic' require_relative 'helpers/magic_test_helper' class MagicTest < Test::Unit::TestCase include MagicTestHelpers def setup @flags_limit = 0xfffffff @magic = Magic.new end def test_magic_singleton_methods [ :open, :mime, :mime_type, :type, :encoding, :compile, :check, :file, :buffer, :descriptor, :fd, :version, :version_array, :version_string, :version_to_a, :version_to_s ].each do |i| assert_respond_to(Magic, i) end end def test_magic_global_singleton_methods [ :do_not_auto_load, :do_not_auto_load=, :do_not_stop_on_error, :do_not_stop_on_error= ].each do |i| assert_respond_to(Magic, i) end end def test_magic_alias assert_same(FileMagic, Magic) end def test_magic_new_instance assert(@magic.class == Magic) end def test_magic_new_instance_default_flags assert_equal(Magic::NONE, @magic.flags) end def test_magic_new_with_block obtained = nil output = capture_stderr do obtained = Magic.new {} end assert_kind_of(Magic, obtained) expected = "Magic::new() does not take block; use Magic::open() instead\n" assert_equal(expected, output.split(/\.rb:\d+\:\s+?|warning:\s+?/).pop) end def test_magic_new_with_do_not_auto_load_set end def test_magic_new_with_MAGIC_DO_NOT_STOP_ON_ERROR_environment_variable end def test_magic_new_with_MAGIC_DO_NOT_AUTOLOAD_environment_variable end def test_magic_instance_methods [ :do_not_stop_on_error, :do_not_stop_on_error=, :open?, :close, :closed?, :paths, :get_parameter, :set_parameter, :flags, :flags=, :flags_list, :flags_names, :flags_to_a, :file, :buffer, :descriptor, :fd, :load, :load_files, :load_buffers, :loaded?, :compile, :check, :valid? ].each do |i| assert_respond_to(@magic, i) end end def test_magic_string_integration_methods [ :magic, :mime, :type ].each do |i| assert_respond_to(String.allocate, i) end end def test_magic_file_integration_singleton_methods [ :magic, :mime, :type ].each do |i| assert_respond_to(File, i) end end def test_magic_file_integration_instance_methods [ :magic, :mime, :type ].each do |i| assert_respond_to(File.allocate, i) end end def test_magic_open? assert_true(@magic.open?) @magic.close assert_false(@magic.open?) end def test_magic_close @magic.close assert_true(@magic.closed?) end def test_magic_close_twice assert_nothing_raised do @magic.close @magic.close end end def test_magic_close_error error = assert_raise Magic::LibraryError do @magic.close @magic.flags end assert_equal('Magic library is not open', error.message) end def test_magic_closed? assert_false(@magic.closed?) @magic.close assert_true(@magic.closed?) end def test_magic_inspect_when_magic_open assert_match(%r{^#$}, @magic.inspect) end def test_magic_inspect_when_magic_closed @magic.close assert_match(%r{^#$}, @magic.inspect) end def test_magic_paths assert_kind_of(Array, @magic.paths) assert_not_equal(0, @magic.paths.size) end def test_magic_paths_with_MAGIC_environment_variable with_fixtures do magic = with_env({"MAGIC" => "png-fake.magic"}) do Magic.new end assert_equal(["png-fake.magic"], magic.paths) assert_match(%r{^Ruby Gem image}, magic.file(Pathname.new('ruby.png'))) end end def test_magic_get_parameter_with_PARAM_INDIR_MAX assert_equal(50, @magic.get_parameter(Magic::PARAM_INDIR_MAX)) end def test_magic_get_parameter_with_PARAM_BYTES_MAX assert_equal(1024 * 1024, @magic.get_parameter(Magic::PARAM_BYTES_MAX)) end def test_magic_get_parameter_lower_boundary error = assert_raise Magic::ParameterError do @magic.get_parameter(-1) end assert_equal('unknown or invalid parameter specified', error.message) end def test_magic_get_parameter_upper_boundary error = assert_raise Magic::ParameterError do @magic.get_parameter(128) end assert_equal('unknown or invalid parameter specified', error.message) end def test_magic_set_parameter_with_PARAM_INDIR_MAX @magic.set_parameter(Magic::PARAM_INDIR_MAX, 128) assert_equal(128, @magic.get_parameter(Magic::PARAM_INDIR_MAX)) end def test_magic_set_parameter_with_PARAM_BYTES_MAX assert_nothing_raised do @magic.set_parameter(Magic::PARAM_BYTES_MAX, 1024 * 1024 * 10) end assert_equal(1024 * 1024 * 10, @magic.get_parameter(Magic::PARAM_BYTES_MAX)) end def test_magic_set_parameter_lower_boundary error = assert_raise Magic::ParameterError do @magic.set_parameter(-1, 0) end assert_equal('unknown or invalid parameter specified', error.message) assert_equal(Errno::EINVAL::Errno, error.errno) end def test_magic_set_parameter_upper_boundary error = assert_raise Magic::ParameterError do @magic.set_parameter(128, 0) end assert_equal('unknown or invalid parameter specified', error.message) assert_equal(Errno::EINVAL::Errno, error.errno) end def test_magic_set_parameter_value_lower_boundary assert_nothing_raised do @magic.set_parameter(0, 0) end end def test_magic_set_parameter_value_upper_boundary error = assert_raise Magic::ParameterError do @magic.set_parameter(0, -1) end assert_equal('invalid parameter value specified', error.message) assert_equal(Errno::EOVERFLOW::Errno, error.errno) end def test_magic_set_parameter_overflow_with_PARAM_INDIR_MAX error = assert_raise Magic::ParameterError do @magic.set_parameter(Magic::PARAM_INDIR_MAX, 128 * 1024) end assert_equal('invalid parameter value specified', error.message) assert_equal(Errno::EOVERFLOW::Errno, error.errno) end def test_magic_flags end def test_magic_flags_with_NONE_flag @magic.flags = 0x000000 assert_kind_of(Integer, @magic.flags) assert_equal(Magic::NONE, @magic.flags) end def test_magic_flags_with_DEBUG_flag output = capture_stderr do @magic.flags = Magic::DEBUG end assert_kind_of(Integer, @magic.flags) assert_equal(Magic::DEBUG, @magic.flags) assert_match(%r{^(.+)?Magic::DEBUG flag is set;}, output) end def test_magic_flags_with_MIME_TYPE_flag @magic.flags = 0x000010 assert_kind_of(Integer, @magic.flags) assert_equal(Magic::MIME_TYPE, @magic.flags) end def test_magic_flags_with_CHECK_flag output = capture_stderr do @magic.flags = Magic::CHECK end assert_kind_of(Integer, @magic.flags) assert_equal(Magic::CHECK, @magic.flags) assert_match(%r{^(.+)?Magic::CHECK flag is set;}, output) end def test_magic_flags_with_MIME_ENCODING_flag @magic.flags = 0x000400 assert_kind_of(Integer, @magic.flags) assert_equal(Magic::MIME_ENCODING, @magic.flags) end def test_magic_flags_with_MIME_flag @magic.flags = 0x000410 assert_kind_of(Integer, @magic.flags) assert_equal(Magic::MIME, @magic.flags) end def test_magic_flags_list end def test_magic_flags_list_alias assert_alias_method(@magic, :flags_to_a, :flags_list) end def test_magic_flags_list_with_NONE_flag @magic.flags = Magic::NONE assert_kind_of(Array, @magic.flags_list) assert_equal([Magic::NONE], @magic.flags_list) end def test_magic_flags_list_with_MIME_TYPE_flag @magic.flags = Magic::MIME_TYPE assert_kind_of(Array, @magic.flags_list) assert_equal([Magic::MIME_TYPE], @magic.flags_list) end def test_magic_flags_list_with_MIME_ENCODING_flag @magic.flags = Magic::MIME_ENCODING assert_kind_of(Array, @magic.flags_list) assert_equal([Magic::MIME_ENCODING], @magic.flags_list) end def test_magic_flags_list_with_MIME_flag @magic.flags = Magic::MIME_TYPE | Magic::MIME_ENCODING assert_kind_of(Array, @magic.flags_list) assert_equal([Magic::MIME_TYPE, Magic::MIME_ENCODING], @magic.flags_list) end def test_magic_flags_list_with_NONE_flag_and_argument_true @magic.flags = Magic::NONE assert_kind_of(Array, @magic.flags_list) assert_equal(['NONE'], @magic.flags_list(true)) end def test_magic_flags_list_with_MIME_TYPE_flag_and_argument_true @magic.flags = Magic::MIME_TYPE assert_kind_of(Array, @magic.flags_list) assert_equal(['MIME_TYPE'], @magic.flags_list(true)) end def test_magic_flags_list_with_MIME_ENCODING_flag_and_argument_true @magic.flags = Magic::MIME_ENCODING assert_kind_of(Array, @magic.flags_list) assert_equal(['MIME_ENCODING'], @magic.flags_list(true)) end def test_magic_flags_list_with_MIME_flag_and_argument_true @magic.flags = Magic::MIME_TYPE | Magic::MIME_ENCODING assert_kind_of(Array, @magic.flags_list) assert_equal(['MIME_TYPE', 'MIME_ENCODING'], @magic.flags_list(true)) end def test_magic_flags_names end def test_magic_flags_names_with_NONE_flag end def test_magic_flags_names_with_MIME_TYPE_flag end def test_magic_flags_names_with_MIME_ENCODING_flag end def test_magic_flags_names_with_MIME_flag end def test_magic_flags_error_lower_boundary error = assert_raise Magic::FlagsError do @magic.flags = -@flags_limit end assert_equal('unknown or invalid flag specified', error.message) assert_equal(Errno::EINVAL::Errno, error.errno) end def test_magic_flags_error_upper_boundary error = assert_raise Magic::FlagsError do @magic.flags = @flags_limit + 1 end assert_equal('unknown or invalid flag specified', error.message) assert_equal(Errno::EINVAL::Errno, error.errno) end def test_magic_do_not_stop_on_error_with_truthy_values end def test_magic_do_not_stop_on_error end def test_magic_do_not_stop_on_error_set_globally end def test_magic_file end def test_magic_file_with_do_not_stop_on_error_set end def test_magic_file_with_do_not_stop_on_error_set_globally end def test_magic_file_with_magic_library_not_loaded end def test_magic_file_with_nil_argument error = assert_raise TypeError do @magic.file nil end assert_equal('wrong argument type nil (expected String or IO-like object)', error.message) end def test_magic_file_argument_with_NULL assert_raise ArgumentError do @magic.file "string\0value" end end def test_magic_file_with_path_like_argument end def test_magic_file_with_IO_like_argument with_fixtures do @magic.load('png-fake.magic') File.open('ruby.png') do |file| assert_match(%r{^Ruby Gem image}, @magic.file(file)) assert_false(file.closed?) end end end def test_magic_file_with_IO_like_argument_stream_closed error = assert_raise IOError do IO.pipe do |r, w| r.close @magic.file r end end assert_equal('closed stream', error.message) end def test_magic_file_with_String_like_argument require 'pathname' with_fixtures do @magic.load('png-fake.magic') assert_equal(['png-fake.magic'], @magic.paths) assert_match(%r{^Ruby Gem image}, @magic.file(Pathname.new('ruby.png'))) end with_fixtures do @magic.load('png-fake.magic', 'shell.magic') assert_equal(['png-fake.magic', 'shell.magic'], @magic.paths) assert_match(%r{^Ruby Gem image}, @magic.file(Pathname.new('ruby.png'))) end end def test_magic_file_with_ERROR_flag end def test_magic_file_with_MAGIC_CONTINUE_flag end def test_magic_file_with_EXTENSION_flag end def test_magic_buffer end def test_magic_buffer_with_magic_library_not_loaded end def test_magic_buffer_with_nil_argument error = assert_raise TypeError do @magic.buffer nil end assert_equal('wrong argument type nil (expected String)', error.message) end def test_magic_buffer_argument_with_NULL assert_nothing_raised do @magic.buffer "string\0value" end end def test_magic_buffer_with_MAGIC_CONTINUE_flag end def test_magic_buffer_with_EXTENSION_flag end def test_magic_descriptor with_fixtures do @magic.load('png-fake.magic') File.open('ruby.png') do |file| assert_match(%r{^Ruby Gem image}, @magic.descriptor(file.fileno)) assert_false(file.closed?) end end end def test_magic_descriptor_alias assert_alias_method(@magic, :fd, :descriptor) end def test_magic_descriptor_with_magic_library_not_loaded end def test_magic_descriptor_with_nil_argument error = assert_raise TypeError do @magic.descriptor nil end expected = format('wrong argument type nil (expected %s)', 0.class) assert_equal(expected, error.message) end def test_magic_descriptor_with_invalid_descriptor error = assert_raise IOError do @magic.descriptor(-1) end assert_equal('Bad file descriptor', error.message) end def test_magic_descriptor_with_descriptor_closed error = assert_raise IOError do IO.pipe do |r, w| r.close @magic.descriptor r.fileno end end assert_equal('closed stream', error.message) end def test_magic_descriptor_with_old_descriptor_open end def test_magic_descriptor_with_MAGIC_CONTINUE_flag end def test_magic_descriptor_with_EXTENSION_flag end def test_magic_fd_with_integer end def test_magic_fd_with_object end def test_magic_load end def test_magic_load_alias assert_alias_method(@magic, :load_files, :load) end def test_magic_load_with_DEBUG_flag capture_stderr do @magic.flags = Magic::DEBUG end assert_kind_of(Integer, @magic.flags) assert_equal(Magic::DEBUG, @magic.flags) output = capture_stderr(children: true) do with_fixtures do assert_raise Magic::MagicError do @magic.load('invalid.magic') end end end assert_match(%r{^.+Not a valid Magic file!}, output) end def test_magic_load_with_do_not_auto_load_set end def test_magic_load_with_array_argument end def test_magic_load_with_custom_Magic_file_path end def test_magic_with_new_instance_custom_Magic_file_path end def test_magic_load_with_MAGIC_environment_variable original_paths = @magic.paths with_fixtures do with_env({"MAGIC" => "png-fake.magic"}) do # assert on setup magic = Magic.new assert_equal(["png-fake.magic"], magic.paths) assert_match(%r{^Ruby Gem image}, magic.file(Pathname.new('ruby.png'))) # verify that #load should override the existing @paths and MAGIC magic.load(original_paths) assert_equal(original_paths, magic.paths) assert_match(%r{^PNG image data}, magic.file(Pathname.new('ruby.png'))) end end end def test_magic_load_buffers end def test_magic_load_buffers_with_array_argument end def test_magic_load_buffers_with_DEBUG_flag end def test_magic_loaded? end def test_magic_loaded_with_do_not_auto_load_set end def test_magic_compile end def test_magic_compile_with_DEBUG_flag end def test_magic_check end def test_magic_check_alias assert_alias_method(@magic, :valid?, :check) end def test_magic_check_with_DEBUG_flag output = capture_stderr(children: true) do @magic.flags = Magic::DEBUG end assert_kind_of(Integer, @magic.flags) assert_equal(Magic::DEBUG, @magic.flags) assert_match(%r{(.+)?Magic::DEBUG flag is set;}, output) output = capture_stderr(children: true) do with_fixtures do assert_nothing_raised do @magic.check('invalid.magic') end end end assert_match(%r{^.+Not a valid Magic file!}, output) end def test_magic_version assert_kind_of(Integer, Magic.version) assert(Magic.version > 0) end def test_magic_version_array expected = [Magic.version / 100, Magic.version % 100] assert_equal(expected, Magic.version_array) end def test_magic_version_array_alias assert_alias_method(Magic, :version_array, :version_to_a) end def test_magic_version_string expected = '%d.%02d' % Magic.version_array assert_equal(expected, Magic.version_string) end def test_magic_version_string_alias assert_alias_method(Magic, :version_string, :version_to_s) end def test_magic_singleton_do_not_auto_load_global_with_truthy_values assert_false(Magic.do_not_auto_load) Magic.do_not_auto_load = 1 Magic.do_not_auto_load = 0 assert_true(Magic.do_not_auto_load) Magic.do_not_auto_load = false assert_false(Magic.do_not_auto_load) end def test_magic_singleton_do_not_auto_load_global omit_unless(Process.respond_to?(:fork), "Platform does not support fork") saved = Magic.do_not_auto_load klass = Class.new(Magic) assert_false(Magic.do_not_auto_load) assert_false(klass.do_not_auto_load) fork do Magic.do_not_auto_load = true magic = Magic.new error_1 = assert_raise Magic::MagicError do magic.buffer '' end error_2 = assert_raise Magic::MagicError do klass.buffer '' end assert_equal('Magic library not loaded', error_1.message) assert_equal('Magic library not loaded', error_2.message) assert_true(Magic.do_not_auto_load) assert_true(klass.do_not_auto_load) end Process.waitpid rescue Errno::ECHILD assert_false(Magic.do_not_auto_load) assert_false(klass.do_not_auto_load) Magic.do_not_auto_load = saved assert_false(Magic.do_not_auto_load) assert_false(klass.do_not_auto_load) end def test_magic_singleton_do_not_auto_load_global_no_fork omit_if(Process.respond_to?(:fork), "Platform supports fork") assert_false(Magic.do_not_auto_load) saved = Magic.do_not_auto_load klass = Class.new(Magic) assert_false(Magic.do_not_auto_load) assert_false(klass.do_not_auto_load) Magic.do_not_auto_load = true magic = Magic.new error_1 = assert_raise Magic::MagicError do magic.buffer '' end error_2 = assert_raise Magic::MagicError do klass.buffer '' end assert_equal('Magic library not loaded', error_1.message) assert_equal('Magic library not loaded', error_2.message) assert_true(Magic.do_not_auto_load) assert_true(klass.do_not_auto_load) Magic.do_not_auto_load = saved assert_false(Magic.do_not_auto_load) assert_false(klass.do_not_auto_load) end def test_magic_singleton_do_not_stop_on_error_with_truthy_values end def test_magic_singleton_do_not_stop_on_error_global end def test_magic_singleton_do_not_stop_on_error end def test_magic_singleton_open end def test_magic_singleton_open_custom_flag end def test_magic_singleton_open_block end def test_magic_singleton_open_block_custom_flag end def test_magic_singleton_mime end def test_magic_singleton_mime_block end def test_magic_singleton_type end def test_magic_singleton_type_block end def test_magic_singleton_encoding end def test_magic_singleton_encoding_block end def test_magic_singleton_compile end def test_magic_singleton_check end def test_magic_singleton_file end def test_magic_singleton_buffer end def test_magic_singleton_descriptor end def test_magic_magic_error message = 'The quick brown fox jumps over the lazy dog' error = Magic::Error.new(message) assert_respond_to(error, :errno) assert_equal(message, error.message) assert_nil(error.errno) end def test_magic_error_attribute_errno @magic.flags = @flags_limit + 1 rescue Magic::Error => error assert_kind_of(Magic::FlagsError, error) assert_equal(Errno::EINVAL::Errno, error.errno) end def test_magic_new_instance_with_arguments end def test_file_integration_magic end def test_file_integration_magic_with_custom_flag end def test_file_integration_mime end def test_file_integration_type end def test_file_integration_singleton_magic end def test_file_integration_singleton_magic_with_custom_flag end def test_file_integration_singleton_mime end def test_file_integration_singleton_type end def test_string_integration_magic end def test_string_integration_magic_with_custom_flag end def test_string_integration_mime end def test_string_integration_type end def test_gc_compaction omit_unless( defined?(GC.verify_compaction_references) == 'method', "Platform does not support GC.compact" ) GC.verify_compaction_references(double_heap: true, toward: :empty) end end