├── lib ├── iconv │ └── version.rb └── iconv.rb ├── .github ├── release.yml ├── dependabot.yml └── workflows │ ├── test.yml │ └── push_gem.yml ├── Gemfile ├── .gitignore ├── test ├── utils.rb ├── test_option.rb ├── test_partial.rb └── test_basic.rb ├── iconv.gemspec ├── Rakefile ├── BSDL ├── ext └── iconv │ ├── mkwrapper.rb │ ├── extconf.rb │ ├── charset_alias.rb │ └── iconv.c ├── README.md └── COPYING /lib/iconv/version.rb: -------------------------------------------------------------------------------- 1 | class Iconv 2 | VERSION = "1.1.1" 3 | end 4 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | exclude: 3 | labels: 4 | - dependencies # Added by Dependabot 5 | -------------------------------------------------------------------------------- /lib/iconv.rb: -------------------------------------------------------------------------------- 1 | require "iconv/iconv.so" 2 | require "iconv/version" 3 | 4 | class Iconv 5 | # Your code goes here... 6 | end 7 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: 'github-actions' 4 | directory: '/' 5 | schedule: 6 | interval: 'weekly' 7 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in iconv.gemspec 4 | gemspec 5 | 6 | gem 'rake' 7 | 8 | group :development, :test do 9 | gem 'test-unit' 10 | end 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.o 3 | *.rbc 4 | *.so 5 | *.bundle 6 | *.swp 7 | .bundle 8 | .config 9 | .yardoc 10 | Gemfile.lock 11 | InstalledFiles 12 | Makefile 13 | _yardoc 14 | coverage 15 | doc/ 16 | lib/bundler/man 17 | mkmf.log 18 | pkg 19 | rdoc 20 | spec/reports 21 | test/tmp 22 | test/version_tmp 23 | tmp 24 | -------------------------------------------------------------------------------- /test/utils.rb: -------------------------------------------------------------------------------- 1 | require 'iconv' 2 | require 'test/unit' 3 | 4 | class TestIconv < ::Test::Unit::TestCase 5 | if defined?(::Encoding) and String.method_defined?(:force_encoding) 6 | def self.encode(str, enc) 7 | str.force_encoding(enc) 8 | end 9 | else 10 | def self.encode(str, enc) 11 | str 12 | end 13 | end 14 | 15 | def default_test 16 | self.class == TestIconv or super 17 | end 18 | 19 | ASCII = "ascii" 20 | EUCJ_STR = encode("\xa4\xa2\xa4\xa4\xa4\xa6\xa4\xa8\xa4\xaa", "EUC-JP").freeze 21 | SJIS_STR = encode("\x82\xa0\x82\xa2\x82\xa4\x82\xa6\x82\xa8", "Shift_JIS").freeze 22 | end if defined?(::Iconv) 23 | -------------------------------------------------------------------------------- /iconv.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'iconv/version' 5 | 6 | Gem::Specification.new do |gem| 7 | gem.name = "iconv" 8 | gem.version = Iconv::VERSION 9 | gem.authors = ["NARUSE, Yui"] 10 | gem.email = ["naruse@airemix.jp"] 11 | gem.description = %q{iconv wrapper library} 12 | gem.summary = %q{iconv wrapper library} 13 | gem.homepage = "https://github.com/ruby/iconv" 14 | 15 | gem.files = `git ls-files`.split($/) 16 | gem.extensions = ['ext/iconv/extconf.rb'] 17 | gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) } 18 | gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) 19 | gem.required_ruby_version = '>= 1.8.7' 20 | gem.require_paths = ["lib"] 21 | end 22 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | ruby-versions: 7 | uses: ruby/actions/.github/workflows/ruby_versions.yml@master 8 | with: 9 | engine: cruby-truffleruby 10 | min_version: 2.3 11 | test: 12 | needs: ruby-versions 13 | name: build (${{ matrix.ruby }} / ${{ matrix.os }}) 14 | strategy: 15 | matrix: 16 | ruby: ${{ fromJson(needs.ruby-versions.outputs.versions) }} 17 | os: [ ubuntu-latest, macos-15-intel, macos-latest ] 18 | exclude: 19 | - ruby: 2.3 20 | os: macos-latest 21 | - ruby: 2.4 22 | os: macos-latest 23 | - ruby: 2.5 24 | os: macos-latest 25 | runs-on: ${{ matrix.os }} 26 | steps: 27 | - uses: actions/checkout@v6 28 | - name: Set up Ruby 29 | uses: ruby/setup-ruby@v1 30 | with: 31 | ruby-version: ${{ matrix.ruby }} 32 | - name: Install dependencies 33 | run: | 34 | bundle install 35 | - name: Run compile and test 36 | run: rake 37 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require 'rake/testtask' 3 | require 'rake/clean' 4 | 5 | NAME = 'iconv' 6 | 7 | # rule to build the extension: this says 8 | # that the extension should be rebuilt 9 | # after any change to the files in ext 10 | file "lib/#{NAME}/#{NAME}.#{RbConfig::CONFIG['DLEXT']}" => 11 | Dir.glob("ext/#{NAME}/*{.rb,.c}") do 12 | Dir.chdir("ext/#{NAME}") do 13 | # this does essentially the same thing 14 | # as what RubyGems does 15 | ruby "extconf.rb", *ARGV.grep(/\A--/) 16 | sh "make", *ARGV.grep(/\A(?!--)/) 17 | end 18 | cp "ext/#{NAME}/#{NAME}.#{RbConfig::CONFIG['DLEXT']}", "lib/#{NAME}" 19 | end 20 | 21 | # make the :test task depend on the shared 22 | # object, so it will be built automatically 23 | # before running the tests 24 | task :test => "lib/#{NAME}/#{NAME}.#{RbConfig::CONFIG['DLEXT']}" 25 | 26 | # use 'rake clean' and 'rake clobber' to 27 | # easily delete generated files 28 | CLEAN.include("ext/**/*{.o,.log,.#{RbConfig::CONFIG['DLEXT']}}") 29 | CLEAN.include("ext/**/Makefile") 30 | CLOBBER.include("lib/**/*.#{RbConfig::CONFIG['DLEXT']}") 31 | 32 | # the same as before 33 | Rake::TestTask.new do |t| 34 | t.libs << 'test' 35 | end 36 | 37 | desc "Run tests" 38 | task :default => :test 39 | -------------------------------------------------------------------------------- /test/test_option.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../utils.rb", __FILE__) 2 | 3 | class TestIconv::Option < TestIconv 4 | def test_ignore_option 5 | begin 6 | iconv = Iconv.new('SHIFT_JIS', 'EUC-JP') 7 | iconv.transliterate? 8 | rescue NotImplementedError 9 | return 10 | end 11 | iconv = Iconv.new('SHIFT_JIS', 'EUC-JP//ignore') 12 | str = iconv.iconv(EUCJ_STR) 13 | str << iconv.iconv(nil) 14 | assert_equal(SJIS_STR, str) 15 | iconv.close 16 | 17 | iconv = Iconv.new('SHIFT_JIS//IGNORE', 'EUC-JP//ignore') 18 | str = iconv.iconv(EUCJ_STR) 19 | str << iconv.iconv(nil) 20 | assert_equal(SJIS_STR, str) 21 | iconv.close 22 | end 23 | 24 | def test_translit_option 25 | begin 26 | iconv = Iconv.new('SHIFT_JIS', 'EUC-JP') 27 | iconv.transliterate? 28 | rescue NotImplementedError 29 | return 30 | end 31 | iconv = Iconv.new('SHIFT_JIS', 'EUC-JP//ignore') 32 | str = iconv.iconv(EUCJ_STR) 33 | str << iconv.iconv(nil) 34 | assert_equal(SJIS_STR, str) 35 | iconv.close 36 | 37 | iconv = Iconv.new('SHIFT_JIS//TRANSLIT', 'EUC-JP//translit//ignore') 38 | str = iconv.iconv(EUCJ_STR) 39 | str << iconv.iconv(nil) 40 | assert_equal(SJIS_STR, str) 41 | iconv.close 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /.github/workflows/push_gem.yml: -------------------------------------------------------------------------------- 1 | name: Publish gem to rubygems.org 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | permissions: 9 | contents: read 10 | 11 | jobs: 12 | push: 13 | if: github.repository == 'ruby/iconv' 14 | runs-on: ubuntu-latest 15 | 16 | environment: 17 | name: rubygems.org 18 | url: https://rubygems.org/gems/iconv 19 | 20 | permissions: 21 | contents: write 22 | id-token: write 23 | 24 | steps: 25 | - name: Harden Runner 26 | uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 27 | with: 28 | egress-policy: audit 29 | 30 | - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 31 | 32 | - name: Set up Ruby 33 | uses: ruby/setup-ruby@d5126b9b3579e429dd52e51e68624dda2e05be25 # v1.267.0 34 | with: 35 | bundler-cache: true 36 | ruby-version: ruby 37 | 38 | - name: Publish to RubyGems 39 | uses: rubygems/release-gem@1c162a739e8b4cb21a676e97b087e8268d8fc40b # v1.1.2 40 | 41 | - name: Create GitHub release 42 | run: | 43 | tag_name="$(git describe --tags --abbrev=0)" 44 | gh release create "${tag_name}" --verify-tag --generate-notes 45 | env: 46 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 47 | -------------------------------------------------------------------------------- /BSDL: -------------------------------------------------------------------------------- 1 | Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions 5 | are met: 6 | 1. Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | 2. Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 13 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 14 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 15 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 16 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 17 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 18 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 19 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 20 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 21 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 22 | SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /ext/iconv/mkwrapper.rb: -------------------------------------------------------------------------------- 1 | #! /usr/bin/ruby 2 | require 'rbconfig' 3 | require 'optparse' 4 | 5 | # http://www.ctan.org/get/macros/texinfo/texinfo/gnulib/lib/config.charset 6 | # Tue, 25 Dec 2007 00:00:00 GMT 7 | 8 | HEADER = < iconv(cd,0,0,0,0) <---") 17 | src = xpopen(cpp_command("")) {|f|f.read} 18 | if !(func = src[/^--->\s*(\w+).*\s*<---/, 1]) 19 | Logging::message "iconv function name not found" 20 | false 21 | elsif !(second = src[%r"\b#{func}\s*\(.*?,(.*?),.*?\)\s*;"m, 1]) 22 | Logging::message "prototype for #{func}() not found" 23 | false 24 | else 25 | Logging::message $&+"\n" 26 | /\bconst\b/ =~ second 27 | end 28 | end 29 | $defs.push('-DICONV_INPTR_CONST') 30 | end 31 | have_func("iconvlist", "iconv.h") 32 | have_func("__iconv_free_list", "iconv.h") 33 | if conf 34 | prefix = '$(srcdir)' 35 | prefix = $nmake ? "{#{prefix}}" : "#{prefix}/" 36 | if $extout 37 | wrapper = "$(RUBYARCHDIR)/iconv.rb" 38 | else 39 | wrapper = "./iconv.rb" 40 | $INSTALLFILES = [[wrapper, "$(RUBYARCHDIR)"]] 41 | end 42 | if String === conf 43 | require 'uri' 44 | scheme = URI.parse(conf).scheme 45 | else 46 | conf = "$(srcdir)/config.charset" 47 | end 48 | $cleanfiles << wrapper 49 | end 50 | create_makefile("iconv/iconv") 51 | if conf 52 | open("Makefile", "a") do |mf| 53 | mf.print("\nall: #{wrapper}\n\n#{wrapper}: #{prefix}charset_alias.rb") 54 | mf.print(" ", conf) unless scheme 55 | mf.print("\n\t$(RUBY) $(srcdir)/charset_alias.rb #{conf} $@\n") 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /test/test_basic.rb: -------------------------------------------------------------------------------- 1 | # coding: US-ASCII 2 | # This file needs to be compatible with Ruby 1.8 3 | require File.expand_path("../utils.rb", __FILE__) 4 | 5 | class TestIconv::Basic < TestIconv 6 | def test_euc2sjis 7 | iconv = Iconv.open('SHIFT_JIS', 'EUC-JP') 8 | str = iconv.iconv(EUCJ_STR) 9 | str << iconv.iconv(nil) 10 | assert_equal(SJIS_STR, str) 11 | iconv.close 12 | end 13 | 14 | def test_close 15 | iconv = Iconv.new('Shift_JIS', 'EUC-JP') 16 | output = "" 17 | begin 18 | output += iconv.iconv(EUCJ_STR) 19 | output += iconv.iconv(nil) 20 | ensure 21 | assert_respond_to(iconv, :close) 22 | assert_equal("", iconv.close) 23 | assert_equal(SJIS_STR, output) 24 | end 25 | end 26 | 27 | def test_open_without_block 28 | assert_respond_to(Iconv, :open) 29 | iconv = Iconv.open('SHIFT_JIS', 'EUC-JP') 30 | str = iconv.iconv(EUCJ_STR) 31 | str << iconv.iconv(nil) 32 | assert_equal(SJIS_STR, str ) 33 | iconv.close 34 | end 35 | 36 | def test_open_with_block 37 | input = "#{EUCJ_STR}\n"*2 38 | output = "" 39 | Iconv.open("Shift_JIS", "EUC-JP") do |cd| 40 | input.each_line do |s| 41 | output << cd.iconv(s) 42 | end 43 | output << cd.iconv(nil) 44 | end 45 | assert_equal("#{SJIS_STR}\n"*2, output) 46 | end 47 | 48 | def test_invalid_arguments 49 | assert_raise(TypeError) { Iconv.new(nil, 'Shift_JIS') } 50 | assert_raise(TypeError) { Iconv.new('Shift_JIS', nil) } 51 | assert_raise(TypeError) { Iconv.open(nil, 'Shift_JIS') } 52 | assert_raise(TypeError) { Iconv.open('Shift_JIS', nil) } 53 | end 54 | 55 | def test_unknown_encoding 56 | assert_raise(Iconv::InvalidEncoding) { Iconv.iconv("utf-8", "X-UKNOWN", "heh") } 57 | assert_raise(Iconv::InvalidEncoding, '[ruby-dev:39487]') { 58 | Iconv.iconv("X-UNKNOWN-1", "X-UNKNOWN-2") {break} 59 | } 60 | end 61 | 62 | def test_github_issue_16 63 | i = Iconv.new('SHIFT_JISX0213', 'UTF-8') 64 | ret = i.iconv("\xE3\x81\xBB\xE3\x81\x92") 65 | ret << i.iconv(nil) 66 | i.close 67 | assert_equal "\x82\xD9\x82\xB0", ret 68 | rescue Iconv::InvalidEncoding 69 | # ignore if the iconv doesn't support SHIFT_JISX0213 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Iconv 2 | 3 | [![Build Status](https://travis-ci.org/ruby/iconv.svg)](https://travis-ci.org/ruby/iconv) 4 | 5 | iconv wrapper, used to be ext/iconv 6 | 7 | ## Abstract 8 | 9 | Iconv is a wrapper class for the UNIX 95 iconv() function family, 10 | which translates string between various encoding systems. 11 | 12 | See Open Group's on-line documents for more details. 13 | * iconv.h: http://www.opengroup.org/onlinepubs/007908799/xsh/iconv.h.html 14 | * iconv_open(): http://www.opengroup.org/onlinepubs/007908799/xsh/iconv_open.html 15 | * iconv(): http://www.opengroup.org/onlinepubs/007908799/xsh/iconv.html 16 | * iconv_close(): http://www.opengroup.org/onlinepubs/007908799/xsh/iconv_close.html 17 | 18 | Which coding systems are available is platform-dependent. 19 | 20 | ## Installation 21 | 22 | Add this line to your application's Gemfile: 23 | 24 | ```ruby 25 | gem 'iconv' 26 | ``` 27 | 28 | And then execute: 29 | 30 | $ bundle 31 | 32 | Or install it yourself as: 33 | 34 | $ gem install iconv 35 | 36 | ## Usage 37 | 38 | 1. Simple conversion between two charsets. 39 | ```ruby 40 | converted_text = Iconv.conv('iso-8859-15', 'utf-8', text) 41 | ``` 42 | 2. Instantiate a new `Iconv` and use method `Iconv#iconv`. 43 | ```ruby 44 | cd = Iconv.new(to, from) 45 | begin 46 | input.each { |s| output << cd.iconv(s) } 47 | output << cd.iconv(nil) # Don't forget this! 48 | ensure 49 | cd.close 50 | end 51 | ``` 52 | 3. Invoke `Iconv.open` with a block. 53 | ```ruby 54 | Iconv.open(to, from) do |cd| 55 | input.each { |s| output << cd.iconv(s) } 56 | output << cd.iconv(nil) 57 | end 58 | ``` 59 | 4. Shorthand for (3). 60 | ```ruby 61 | Iconv.iconv(to, from, *input.to_a) 62 | ``` 63 | 64 | ## Attentions 65 | 66 | Even if some extensions of implementation dependent are useful, 67 | DON'T USE those extensions in libraries and scripts to widely distribute. 68 | If you want to use those feature, use `String#encode`. 69 | 70 | ## Contributing 71 | 72 | 1. Fork it 73 | 2. Create your feature branch (`git checkout -b my-new-feature`) 74 | 3. Commit your changes (`git commit -am 'Add some feature'`) 75 | 4. Push to the branch (`git push origin my-new-feature`) 76 | 5. Create new Pull Request 77 | 78 | ## License 79 | 80 | Ruby License/2-clause BSDL 81 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Ruby is copyrighted free software by Yukihiro Matsumoto . 2 | You can redistribute it and/or modify it under either the terms of the 3 | 2-clause BSDL (see the file BSDL), or the conditions below: 4 | 5 | 1. You may make and give away verbatim copies of the source form of the 6 | software without restriction, provided that you duplicate all of the 7 | original copyright notices and associated disclaimers. 8 | 9 | 2. You may modify your copy of the software in any way, provided that 10 | you do at least ONE of the following: 11 | 12 | a. place your modifications in the Public Domain or otherwise 13 | make them Freely Available, such as by posting said 14 | modifications to Usenet or an equivalent medium, or by allowing 15 | the author to include your modifications in the software. 16 | 17 | b. use the modified software only within your corporation or 18 | organization. 19 | 20 | c. give non-standard binaries non-standard names, with 21 | instructions on where to get the original software distribution. 22 | 23 | d. make other distribution arrangements with the author. 24 | 25 | 3. You may distribute the software in object code or binary form, 26 | provided that you do at least ONE of the following: 27 | 28 | a. distribute the binaries and library files of the software, 29 | together with instructions (in the manual page or equivalent) 30 | on where to get the original distribution. 31 | 32 | b. accompany the distribution with the machine-readable source of 33 | the software. 34 | 35 | c. give non-standard binaries non-standard names, with 36 | instructions on where to get the original software distribution. 37 | 38 | d. make other distribution arrangements with the author. 39 | 40 | 4. You may modify and include the part of the software into any other 41 | software (possibly commercial). But some files in the distribution 42 | are not written by the author, so that they are not under these terms. 43 | 44 | For the list of those files and their copying conditions, see the 45 | file LEGAL. 46 | 47 | 5. The scripts and library files supplied as input to or produced as 48 | output from the software do not automatically fall under the 49 | copyright of the software, but belong to whomever generated them, 50 | and may be sold commercially, and may be aggregated with this 51 | software. 52 | 53 | 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR 54 | IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 55 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 56 | PURPOSE. 57 | -------------------------------------------------------------------------------- /ext/iconv/charset_alias.rb: -------------------------------------------------------------------------------- 1 | #! /usr/bin/ruby 2 | # :stopdoc: 3 | require 'rbconfig' 4 | require 'optparse' 5 | 6 | # http://www.ctan.org/get/macros/texinfo/texinfo/gnulib/lib/config.charset 7 | # Tue, 25 Dec 2007 00:00:00 GMT 8 | 9 | OS = RbConfig::CONFIG["target_os"] 10 | SHELL = RbConfig::CONFIG['SHELL'] 11 | 12 | class Hash::Ordered < Hash 13 | def [](key) 14 | val = super and val.last 15 | end 16 | def []=(key, val) 17 | ary = fetch(key) {return super(key, [self.size, key, val])} and 18 | ary << val 19 | end 20 | def sort 21 | values.sort.collect {|i, *rest| rest} 22 | end 23 | def each(&block) 24 | sort.each(&block) 25 | end 26 | end 27 | 28 | def charset_alias(config_charset, mapfile, target = OS) 29 | map = Hash::Ordered.new 30 | comments = [] 31 | open(config_charset) do |input| 32 | input.find {|line| /^case "\$os" in/ =~ line} or break 33 | input.find {|line| 34 | /^\s*([-\w\*]+(?:\s*\|\s*[-\w\*]+)*)(?=\))/ =~ line and 35 | $&.split('|').any? {|pattern| File.fnmatch?(pattern.strip, target)} 36 | } or break 37 | input.find do |line| 38 | case line 39 | when /^\s*echo "(?:\$\w+\.)?([-\w*]+)\s+([-\w]+)"/ 40 | sys, can = $1, $2 41 | can.downcase! 42 | map[can] = sys 43 | false 44 | when /^\s*;;/ 45 | true 46 | else 47 | false 48 | end 49 | end 50 | end 51 | case target 52 | when /linux|-gnu/ 53 | # map.delete('ascii') 54 | when /cygwin|os2-emx/ 55 | # get rid of tilde/yen problem. 56 | map['shift_jis'] = 'cp932' 57 | end 58 | st = Hash.new(0) 59 | map = map.sort.collect do |can, *sys| 60 | if sys.grep(/^en_us(?=.|$)/i) {break true} == true 61 | noen = %r"^(?!en_us)\w+_\w+#{Regexp.new($')}$"i #" 62 | sys.reject! {|s| noen =~ s} 63 | end 64 | sys = sys.first 65 | st[sys] += 1 66 | [can, sys] 67 | end 68 | st.delete_if {|sys, i| i == 1}.empty? 69 | st.keys.each {|sys| st[sys] = nil} 70 | st.default = nil 71 | writer = proc do |f| 72 | f.puts("require 'iconv.so'") 73 | f.puts 74 | f.puts(comments) 75 | f.puts("class Iconv") 76 | i = 0 77 | map.each do |can, sys| 78 | if s = st[sys] 79 | sys = s 80 | elsif st.key?(sys) 81 | sys = (st[sys] = "sys#{i+=1}") + " = '#{sys}'.freeze" 82 | else 83 | sys = "'#{sys}'.freeze" 84 | end 85 | f.puts(" charset_map['#{can}'] = #{sys}") 86 | end 87 | f.puts("end") 88 | end 89 | if mapfile 90 | open(mapfile, "w", &writer) 91 | else 92 | writer[STDOUT] 93 | end 94 | end 95 | 96 | target = OS 97 | opt = nil 98 | ARGV.options do |opt2| 99 | opt = opt2 100 | opt.banner << " config.status map.rb" 101 | opt.on("--target OS") {|t| target = t} 102 | opt.parse! and (1..2) === ARGV.size 103 | end or abort opt.to_s 104 | charset_alias(ARGV[0], ARGV[1], target) 105 | -------------------------------------------------------------------------------- /ext/iconv/iconv.c: -------------------------------------------------------------------------------- 1 | /* -*- mode:c; c-file-style:"ruby" -*- */ 2 | /********************************************************************** 3 | 4 | iconv.c - 5 | 6 | $Author$ 7 | created at: Wed Dec 1 20:28:09 JST 1999 8 | 9 | All the files in this distribution are covered under the Ruby's 10 | license (see the file COPYING). 11 | 12 | Documentation by Yukihiro Matsumoto and Gavin Sinclair. 13 | 14 | **********************************************************************/ 15 | 16 | #include "ruby.h" 17 | #include 18 | #include 19 | #include 20 | #ifdef HAVE_RUBY_ENCODING_H 21 | /* assume Ruby 1.9 or later */ 22 | # include "ruby/st.h" 23 | # include "ruby/encoding.h" 24 | #else 25 | # include "st.h" 26 | # include 27 | # define rb_f_notimplement rb_notimplement 28 | # define rb_str_subseq(a, b, c) rb_str_substr(a, b, c) 29 | # define rb_str_new_cstr(a) rb_str_new2(a) 30 | static VALUE 31 | rb_str_equal(str1, str2) 32 | VALUE str1, str2; 33 | { 34 | if (str1 == str2) return Qtrue; 35 | if (TYPE(str2) != T_STRING) { 36 | if (!rb_respond_to(str2, rb_intern("to_str"))) { 37 | return Qfalse; 38 | } 39 | return rb_equal(str2, str1); 40 | } 41 | if (RSTRING(str1)->len == RSTRING(str2)->len && 42 | rb_str_cmp(str1, str2) == 0) { 43 | return Qtrue; 44 | } 45 | return Qfalse; 46 | } 47 | void 48 | rb_set_errinfo(VALUE err) 49 | { 50 | extern VALUE ruby_errinfo; 51 | ruby_errinfo = err; 52 | } 53 | VALUE 54 | rb_sprintf(const char *format, ...) 55 | { 56 | va_list ap; 57 | char *ret; 58 | int len; 59 | 60 | va_start(ap, format); 61 | len = vasprintf(&ret, format, ap); 62 | va_end(ap); 63 | if (len == -1) return Qnil; 64 | 65 | return rb_str_new(ret, len); 66 | } 67 | #endif 68 | #ifndef HAVE_RB_SYS_FAIL_STR 69 | NORETURN(static void rb_sys_fail_str(VALUE msg)); 70 | static void 71 | rb_sys_fail_str(VALUE msg) 72 | { 73 | rb_sys_fail(RSTRING_PTR(msg)); 74 | } 75 | #endif 76 | 77 | #ifdef HAVE_RUBY_ENCODING_H 78 | # define ICONV_ENCODING_SET(obj,idx) rb_ivar_set(obj, id_encindex, INT2FIX(idx)) 79 | static int 80 | iconv_get_encindex(VALUE obj, ID id_encindex) 81 | { 82 | VALUE num_or_nil = rb_ivar_get(obj, id_encindex); 83 | return NIL_P(num_or_nil) ? 0 : FIX2INT(num_or_nil); 84 | } 85 | # define ICONV_ENCODING_GET(obj) iconv_get_encindex(obj, id_encindex) 86 | #else 87 | # define ICONV_ENCODING_GET(a) 0 88 | #endif 89 | 90 | /* 91 | * Document-class: Iconv 92 | * 93 | * == Summary 94 | * 95 | * Ruby extension for charset conversion. 96 | * 97 | * == Abstract 98 | * 99 | * Iconv is a wrapper class for the UNIX 95 iconv() function family, 100 | * which translates string between various encoding systems. 101 | * 102 | * See Open Group's on-line documents for more details. 103 | * * iconv.h: http://www.opengroup.org/onlinepubs/007908799/xsh/iconv.h.html 104 | * * iconv_open(): http://www.opengroup.org/onlinepubs/007908799/xsh/iconv_open.html 105 | * * iconv(): http://www.opengroup.org/onlinepubs/007908799/xsh/iconv.html 106 | * * iconv_close(): http://www.opengroup.org/onlinepubs/007908799/xsh/iconv_close.html 107 | * 108 | * Which coding systems are available is platform-dependent. 109 | * 110 | * == Examples 111 | * 112 | * 1. Simple conversion between two charsets. 113 | * 114 | * converted_text = Iconv.conv('iso-8859-15', 'utf-8', text) 115 | * 116 | * 2. Instantiate a new Iconv and use method Iconv#iconv. 117 | * 118 | * cd = Iconv.new(to, from) 119 | * begin 120 | * input.each { |s| output << cd.iconv(s) } 121 | * output << cd.iconv(nil) # Don't forget this! 122 | * ensure 123 | * cd.close 124 | * end 125 | * 126 | * 3. Invoke Iconv.open with a block. 127 | * 128 | * Iconv.open(to, from) do |cd| 129 | * input.each { |s| output << cd.iconv(s) } 130 | * output << cd.iconv(nil) 131 | * end 132 | * 133 | * 4. Shorthand for (3). 134 | * 135 | * Iconv.iconv(to, from, *input.to_a) 136 | * 137 | * == Attentions 138 | * 139 | * Even if some extensions of implementation dependent are useful, 140 | * DON'T USE those extensions in libraries and scripts to widely distribute. 141 | * If you want to use those feature, use String#encode. 142 | */ 143 | 144 | /* Invalid value for iconv_t is -1 but 0 for VALUE, I hope VALUE is 145 | big enough to keep iconv_t */ 146 | #define VALUE2ICONV(v) ((iconv_t)((VALUE)(v) ^ -1)) 147 | #define ICONV2VALUE(c) ((VALUE)(c) ^ -1) 148 | 149 | struct iconv_env_t 150 | { 151 | iconv_t cd; 152 | int argc; 153 | VALUE *argv; 154 | VALUE ret; 155 | int toidx; 156 | VALUE (*append)_((VALUE, VALUE)); 157 | }; 158 | 159 | struct rb_iconv_opt_t 160 | { 161 | VALUE transliterate; 162 | VALUE discard_ilseq; 163 | }; 164 | 165 | static ID id_transliterate, id_discard_ilseq, id_encindex; 166 | 167 | static VALUE rb_eIconvInvalidEncoding; 168 | static VALUE rb_eIconvFailure; 169 | static VALUE rb_eIconvIllegalSeq; 170 | static VALUE rb_eIconvInvalidChar; 171 | static VALUE rb_eIconvOutOfRange; 172 | static VALUE rb_eIconvBrokenLibrary; 173 | 174 | static ID rb_success, rb_failed; 175 | static VALUE iconv_fail _((VALUE error, VALUE success, VALUE failed, struct iconv_env_t* env, VALUE mesg)); 176 | static VALUE iconv_fail_retry _((VALUE error, VALUE success, VALUE failed, struct iconv_env_t* env, VALUE mesg)); 177 | static VALUE iconv_failure_initialize _((VALUE error, VALUE mesg, VALUE success, VALUE failed)); 178 | static VALUE iconv_failure_success _((VALUE self)); 179 | static VALUE iconv_failure_failed _((VALUE self)); 180 | 181 | static iconv_t iconv_create _((VALUE to, VALUE from, struct rb_iconv_opt_t *opt, int *idx)); 182 | static void iconv_dfree _((void *cd)); 183 | static VALUE iconv_free _((VALUE cd)); 184 | static VALUE iconv_try _((iconv_t cd, const char **inptr, size_t *inlen, char **outptr, size_t *outlen)); 185 | static VALUE rb_str_derive _((VALUE str, const char* ptr, long len)); 186 | static VALUE iconv_convert _((iconv_t cd, VALUE str, long start, long length, int toidx, 187 | struct iconv_env_t* env)); 188 | static VALUE iconv_s_allocate _((VALUE klass)); 189 | static VALUE iconv_initialize _((int argc, VALUE *argv, VALUE self)); 190 | static VALUE iconv_s_open _((int argc, VALUE *argv, VALUE self)); 191 | static VALUE iconv_s_convert _((VALUE self)); 192 | static VALUE iconv_s_iconv _((int argc, VALUE *argv, VALUE self)); 193 | static VALUE iconv_init_state _((VALUE cd)); 194 | static VALUE iconv_finish _((VALUE self)); 195 | static VALUE iconv_iconv _((int argc, VALUE *argv, VALUE self)); 196 | static VALUE iconv_conv _((int argc, VALUE *argv, VALUE self)); 197 | 198 | static VALUE charset_map; 199 | 200 | /* 201 | * Document-method: charset_map 202 | * call-seq: Iconv.charset_map 203 | * 204 | * Returns the map from canonical name to system dependent name. 205 | */ 206 | static VALUE 207 | charset_map_get(VALUE klass) 208 | { 209 | return charset_map; 210 | } 211 | 212 | static VALUE 213 | strip_glibc_option(VALUE *code) 214 | { 215 | VALUE val = StringValue(*code); 216 | const char *ptr = RSTRING_PTR(val), *pend = RSTRING_END(val); 217 | const char *slash = memchr(ptr, '/', pend - ptr); 218 | 219 | if (slash && slash < pend - 1 && slash[1] == '/') { 220 | VALUE opt = rb_str_subseq(val, slash - ptr, pend - slash); 221 | val = rb_str_subseq(val, 0, slash - ptr); 222 | *code = val; 223 | return opt; 224 | } 225 | return 0; 226 | } 227 | 228 | static char * 229 | map_charset(VALUE *code) 230 | { 231 | VALUE val = StringValue(*code); 232 | 233 | if (RHASH_SIZE(charset_map)) { 234 | VALUE data; 235 | VALUE key = rb_funcall2(val, rb_intern("downcase"), 0, 0); 236 | StringValuePtr(key); 237 | data = rb_hash_aref(charset_map, key); 238 | if(!NIL_P(data)) { 239 | *code = (VALUE)data; 240 | } 241 | } 242 | return StringValuePtr(*code); 243 | } 244 | 245 | NORETURN(static void rb_iconv_sys_fail_str(VALUE msg)); 246 | static void 247 | rb_iconv_sys_fail_str(VALUE msg) 248 | { 249 | if (errno == 0) { 250 | rb_exc_raise(iconv_fail(rb_eIconvBrokenLibrary, Qnil, Qnil, NULL, msg)); 251 | } 252 | rb_sys_fail_str(msg); 253 | } 254 | 255 | #define rb_sys_fail_str(s) rb_iconv_sys_fail_str(s) 256 | 257 | NORETURN(static void rb_iconv_sys_fail(const char *s)); 258 | static void 259 | rb_iconv_sys_fail(const char *s) 260 | { 261 | rb_iconv_sys_fail_str(rb_str_new_cstr(s)); 262 | } 263 | 264 | #define rb_sys_fail(s) rb_iconv_sys_fail(s) 265 | 266 | static iconv_t 267 | iconv_create(VALUE to, VALUE from, struct rb_iconv_opt_t *opt, int *idx) 268 | { 269 | VALUE toopt = strip_glibc_option(&to); 270 | VALUE fromopt = strip_glibc_option(&from); 271 | VALUE toenc = 0, fromenc = 0; 272 | const char* tocode = map_charset(&to); 273 | const char* fromcode = map_charset(&from); 274 | iconv_t cd; 275 | int retry = 0; 276 | 277 | #ifdef HAVE_RUBY_ENCODING_H 278 | *idx = rb_enc_find_index(tocode); 279 | #endif 280 | 281 | if (toopt) { 282 | toenc = rb_str_plus(to, toopt); 283 | tocode = RSTRING_PTR(toenc); 284 | } 285 | if (fromopt) { 286 | fromenc = rb_str_plus(from, fromopt); 287 | fromcode = RSTRING_PTR(fromenc); 288 | } 289 | while ((cd = iconv_open(tocode, fromcode)) == (iconv_t)-1) { 290 | int inval = 0; 291 | switch (errno) { 292 | case EMFILE: 293 | case ENFILE: 294 | case ENOMEM: 295 | if (!retry++) { 296 | rb_gc(); 297 | continue; 298 | } 299 | break; 300 | case EINVAL: 301 | retry = 0; 302 | inval = 1; 303 | if (toenc) { 304 | tocode = RSTRING_PTR(to); 305 | rb_str_resize(toenc, 0); 306 | toenc = 0; 307 | continue; 308 | } 309 | if (fromenc) { 310 | fromcode = RSTRING_PTR(from); 311 | rb_str_resize(fromenc, 0); 312 | fromenc = 0; 313 | continue; 314 | } 315 | break; 316 | } 317 | { 318 | const char *s = inval ? "invalid encoding " : "iconv"; 319 | VALUE msg = rb_sprintf("%s(\"%s\", \"%s\")", 320 | s, RSTRING_PTR(to), RSTRING_PTR(from)); 321 | if (!inval) rb_sys_fail_str(msg); 322 | rb_exc_raise(iconv_fail(rb_eIconvInvalidEncoding, Qnil, 323 | rb_ary_new3(2, to, from), NULL, msg)); 324 | } 325 | } 326 | 327 | if (toopt || fromopt) { 328 | if (toopt && fromopt && RTEST(rb_str_equal(toopt, fromopt))) { 329 | fromopt = 0; 330 | } 331 | if (toopt && fromopt) { 332 | rb_warning("encoding option isn't portable: %s, %s", 333 | RSTRING_PTR(toopt) + 2, RSTRING_PTR(fromopt) + 2); 334 | } 335 | else { 336 | rb_warning("encoding option isn't portable: %s", 337 | (toopt ? RSTRING_PTR(toopt) : RSTRING_PTR(fromopt)) + 2); 338 | } 339 | } 340 | 341 | if (opt) { 342 | #ifdef ICONV_SET_TRANSLITERATE 343 | if (opt->transliterate != Qundef) { 344 | int flag = RTEST(opt->transliterate); 345 | rb_warning("encoding option isn't portable: transliterate"); 346 | if (iconvctl(cd, ICONV_SET_TRANSLITERATE, (void *)&flag)) 347 | rb_sys_fail("ICONV_SET_TRANSLITERATE"); 348 | } 349 | #endif 350 | #ifdef ICONV_SET_DISCARD_ILSEQ 351 | if (opt->discard_ilseq != Qundef) { 352 | int flag = RTEST(opt->discard_ilseq); 353 | rb_warning("encoding option isn't portable: discard_ilseq"); 354 | if (iconvctl(cd, ICONV_SET_DISCARD_ILSEQ, (void *)&flag)) 355 | rb_sys_fail("ICONV_SET_DISCARD_ILSEQ"); 356 | } 357 | #endif 358 | } 359 | 360 | return cd; 361 | } 362 | 363 | static void 364 | iconv_dfree(void *cd) 365 | { 366 | iconv_close(VALUE2ICONV(cd)); 367 | } 368 | 369 | #define ICONV_FREE iconv_dfree 370 | 371 | static VALUE 372 | iconv_free(VALUE cd) 373 | { 374 | if (cd && iconv_close(VALUE2ICONV(cd)) == -1) 375 | rb_sys_fail("iconv_close"); 376 | return Qnil; 377 | } 378 | 379 | static VALUE 380 | check_iconv(VALUE obj) 381 | { 382 | Check_Type(obj, T_DATA); 383 | if (RDATA(obj)->dfree != ICONV_FREE) { 384 | rb_raise(rb_eArgError, "Iconv expected (%s)", rb_class2name(CLASS_OF(obj))); 385 | } 386 | return (VALUE)DATA_PTR(obj); 387 | } 388 | 389 | static VALUE 390 | iconv_try(iconv_t cd, const char **inptr, size_t *inlen, char **outptr, size_t *outlen) 391 | { 392 | #ifdef ICONV_INPTR_CONST 393 | #define ICONV_INPTR_CAST 394 | #else 395 | #define ICONV_INPTR_CAST (char **) 396 | #endif 397 | size_t ret; 398 | 399 | errno = 0; 400 | ret = iconv(cd, ICONV_INPTR_CAST inptr, inlen, outptr, outlen); 401 | if (ret == (size_t)-1) { 402 | if (!*inlen) 403 | return Qfalse; 404 | switch (errno) { 405 | case E2BIG: 406 | /* try the left in next loop */ 407 | break; 408 | case EILSEQ: 409 | return rb_eIconvIllegalSeq; 410 | case EINVAL: 411 | return rb_eIconvInvalidChar; 412 | case 0: 413 | return rb_eIconvBrokenLibrary; 414 | default: 415 | rb_sys_fail("iconv"); 416 | } 417 | } 418 | else if (*inlen > 0) { 419 | /* something goes wrong */ 420 | return rb_eIconvIllegalSeq; 421 | } 422 | else if (ret) { 423 | return Qnil; /* conversion */ 424 | } 425 | return Qfalse; 426 | } 427 | 428 | #define FAILED_MAXLEN 16 429 | 430 | static VALUE 431 | iconv_failure_initialize(VALUE error, VALUE mesg, VALUE success, VALUE failed) 432 | { 433 | rb_call_super(1, &mesg); 434 | rb_ivar_set(error, rb_success, success); 435 | rb_ivar_set(error, rb_failed, failed); 436 | return error; 437 | } 438 | 439 | static VALUE 440 | iconv_fail(VALUE error, VALUE success, VALUE failed, struct iconv_env_t* env, VALUE mesg) 441 | { 442 | VALUE args[3]; 443 | 444 | if (!NIL_P(mesg)) { 445 | args[0] = mesg; 446 | } 447 | else if (TYPE(failed) != T_STRING || RSTRING_LEN(failed) < FAILED_MAXLEN) { 448 | args[0] = rb_inspect(failed); 449 | } 450 | else { 451 | args[0] = rb_inspect(rb_str_substr(failed, 0, FAILED_MAXLEN)); 452 | rb_str_cat2(args[0], "..."); 453 | } 454 | args[1] = success; 455 | args[2] = failed; 456 | if (env) { 457 | args[1] = env->append(rb_obj_dup(env->ret), success); 458 | if (env->argc > 0) { 459 | *(env->argv) = failed; 460 | args[2] = rb_ary_new4(env->argc, env->argv); 461 | } 462 | } 463 | return rb_class_new_instance(3, args, error); 464 | } 465 | 466 | static VALUE 467 | iconv_fail_retry(VALUE error, VALUE success, VALUE failed, struct iconv_env_t* env, VALUE mesg) 468 | { 469 | error = iconv_fail(error, success, failed, env, mesg); 470 | if (!rb_block_given_p()) rb_exc_raise(error); 471 | rb_set_errinfo(error); 472 | return rb_yield(failed); 473 | } 474 | 475 | static VALUE 476 | rb_str_derive(VALUE str, const char* ptr, long len) 477 | { 478 | VALUE ret; 479 | 480 | if (NIL_P(str)) 481 | return rb_str_new(ptr, len); 482 | if (RSTRING_PTR(str) + RSTRING_LEN(str) == ptr + len) 483 | ret = rb_str_subseq(str, ptr - RSTRING_PTR(str), len); 484 | else 485 | ret = rb_str_new(ptr, len); 486 | OBJ_INFECT(ret, str); 487 | return ret; 488 | } 489 | 490 | static VALUE 491 | iconv_convert(iconv_t cd, VALUE str, long start, long length, int toidx, struct iconv_env_t* env) 492 | { 493 | VALUE ret = Qfalse; 494 | VALUE error = Qfalse; 495 | VALUE rescue; 496 | const char *inptr, *instart; 497 | size_t inlen; 498 | /* I believe ONE CHARACTER never exceed this. */ 499 | char buffer[BUFSIZ]; 500 | char *outptr; 501 | size_t outlen; 502 | 503 | if (cd == (iconv_t)-1) 504 | rb_raise(rb_eArgError, "closed iconv"); 505 | 506 | if (NIL_P(str)) { 507 | /* Reset output pointer or something. */ 508 | inptr = ""; 509 | inlen = 0; 510 | outptr = buffer; 511 | outlen = sizeof(buffer); 512 | error = iconv_try(cd, &inptr, &inlen, &outptr, &outlen); 513 | if (RTEST(error)) { 514 | unsigned int i; 515 | rescue = iconv_fail_retry(error, Qnil, Qnil, env, Qnil); 516 | if (TYPE(rescue) == T_ARRAY) { 517 | str = RARRAY_LEN(rescue) > 0 ? RARRAY_PTR(rescue)[0] : Qnil; 518 | } 519 | if (FIXNUM_P(str) && (i = FIX2INT(str)) <= 0xff) { 520 | char c = i; 521 | str = rb_str_new(&c, 1); 522 | } 523 | else if (!NIL_P(str)) { 524 | StringValue(str); 525 | } 526 | } 527 | 528 | inptr = NULL; 529 | length = 0; 530 | } 531 | else { 532 | long slen; 533 | 534 | StringValue(str); 535 | slen = RSTRING_LEN(str); 536 | inptr = RSTRING_PTR(str); 537 | 538 | inptr += start; 539 | if (length < 0 || length > start + slen) 540 | length = slen - start; 541 | } 542 | instart = inptr; 543 | inlen = length; 544 | 545 | do { 546 | VALUE errmsg = Qnil; 547 | const char *tmpstart = inptr; 548 | outptr = buffer; 549 | outlen = sizeof(buffer); 550 | 551 | error = iconv_try(cd, &inptr, &inlen, &outptr, &outlen); 552 | 553 | if ( 554 | #if SIGNEDNESS_OF_SIZE_T < 0 555 | 0 <= outlen && 556 | #endif 557 | outlen <= sizeof(buffer)) { 558 | outlen = sizeof(buffer) - outlen; 559 | if (NIL_P(error) || /* something converted */ 560 | outlen > (size_t)(inptr - tmpstart) || /* input can't contain output */ 561 | (outlen < (size_t)(inptr - tmpstart) && inlen > 0) || /* something skipped */ 562 | memcmp(buffer, tmpstart, outlen)) /* something differs */ 563 | { 564 | if (NIL_P(str)) { 565 | ret = rb_str_new(buffer, outlen); 566 | #ifdef HAVE_RUBY_ENCODING_H 567 | if (toidx >= 0) rb_enc_associate_index(ret, toidx); 568 | #endif 569 | } 570 | else { 571 | if (ret) { 572 | ret = rb_str_buf_cat(ret, instart, tmpstart - instart); 573 | } 574 | else { 575 | ret = rb_str_new(instart, tmpstart - instart); 576 | #ifdef HAVE_RUBY_ENCODING_H 577 | if (toidx >= 0) rb_enc_associate_index(ret, toidx); 578 | #endif 579 | OBJ_INFECT(ret, str); 580 | } 581 | ret = rb_str_buf_cat(ret, buffer, outlen); 582 | instart = inptr; 583 | } 584 | } 585 | else if (!inlen) { 586 | inptr = tmpstart + outlen; 587 | } 588 | } 589 | else { 590 | /* Some iconv() have a bug, return *outlen out of range */ 591 | errmsg = rb_sprintf("bug?(output length = %ld)", (long)(sizeof(buffer) - outlen)); 592 | error = rb_eIconvOutOfRange; 593 | } 594 | 595 | if (RTEST(error)) { 596 | long len = 0; 597 | 598 | if (!ret) { 599 | ret = rb_str_derive(str, instart, inptr - instart); 600 | #ifdef HAVE_RUBY_ENCODING_H 601 | if (toidx >= 0) rb_enc_associate_index(ret, toidx); 602 | #endif 603 | } 604 | else if (inptr > instart) { 605 | rb_str_cat(ret, instart, inptr - instart); 606 | } 607 | str = rb_str_derive(str, inptr, inlen); 608 | rescue = iconv_fail_retry(error, ret, str, env, errmsg); 609 | if (TYPE(rescue) == T_ARRAY) { 610 | if ((len = RARRAY_LEN(rescue)) > 0) 611 | rb_str_concat(ret, RARRAY_PTR(rescue)[0]); 612 | if (len > 1 && !NIL_P(str = RARRAY_PTR(rescue)[1])) { 613 | StringValue(str); 614 | inlen = length = RSTRING_LEN(str); 615 | instart = inptr = RSTRING_PTR(str); 616 | continue; 617 | } 618 | } 619 | else if (!NIL_P(rescue)) { 620 | rb_str_concat(ret, rescue); 621 | } 622 | break; 623 | } 624 | } while (inlen > 0); 625 | 626 | if (!ret) { 627 | ret = rb_str_derive(str, instart, inptr - instart); 628 | #ifdef HAVE_RUBY_ENCODING_H 629 | if (toidx >= 0) rb_enc_associate_index(ret, toidx); 630 | #endif 631 | } 632 | else if (inptr > instart) { 633 | rb_str_cat(ret, instart, inptr - instart); 634 | } 635 | return ret; 636 | } 637 | 638 | static VALUE 639 | iconv_s_allocate(VALUE klass) 640 | { 641 | return Data_Wrap_Struct(klass, 0, ICONV_FREE, 0); 642 | } 643 | 644 | static VALUE 645 | get_iconv_opt_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, arg)) 646 | { 647 | VALUE name; 648 | #if defined ICONV_SET_TRANSLITERATE || defined ICONV_SET_DISCARD_ILSEQ 649 | VALUE val; 650 | struct rb_iconv_opt_t *opt = (struct rb_iconv_opt_t *)arg; 651 | #endif 652 | 653 | i = rb_Array(i); 654 | name = rb_ary_entry(i, 0); 655 | #if defined ICONV_SET_TRANSLITERATE || defined ICONV_SET_DISCARD_ILSEQ 656 | val = rb_ary_entry(i, 1); 657 | #endif 658 | do { 659 | if (SYMBOL_P(name)) { 660 | ID id = SYM2ID(name); 661 | if (id == id_transliterate) { 662 | #ifdef ICONV_SET_TRANSLITERATE 663 | opt->transliterate = val; 664 | #else 665 | rb_notimplement(); 666 | #endif 667 | break; 668 | } 669 | if (id == id_discard_ilseq) { 670 | #ifdef ICONV_SET_DISCARD_ILSEQ 671 | opt->discard_ilseq = val; 672 | #else 673 | rb_notimplement(); 674 | #endif 675 | break; 676 | } 677 | } 678 | else { 679 | const char *s = StringValueCStr(name); 680 | if (strcmp(s, "transliterate") == 0) { 681 | #ifdef ICONV_SET_TRANSLITERATE 682 | opt->transliterate = val; 683 | #else 684 | rb_notimplement(); 685 | #endif 686 | break; 687 | } 688 | if (strcmp(s, "discard_ilseq") == 0) { 689 | #ifdef ICONV_SET_DISCARD_ILSEQ 690 | opt->discard_ilseq = val; 691 | #else 692 | rb_notimplement(); 693 | #endif 694 | break; 695 | } 696 | } 697 | name = rb_inspect(name); 698 | rb_raise(rb_eArgError, "unknown option - %s", StringValueCStr(name)); 699 | } while (0); 700 | return Qnil; 701 | } 702 | 703 | static void 704 | get_iconv_opt(struct rb_iconv_opt_t *opt, VALUE options) 705 | { 706 | opt->transliterate = Qundef; 707 | opt->discard_ilseq = Qundef; 708 | if (!NIL_P(options)) { 709 | rb_block_call(options, rb_intern("each"), 0, 0, get_iconv_opt_i, (VALUE)opt); 710 | } 711 | } 712 | 713 | #define iconv_ctl(self, func, val) (\ 714 | iconvctl(VALUE2ICONV(check_iconv(self)), func, (void *)&(val)) ? \ 715 | rb_sys_fail(#func) : (void)0) 716 | 717 | /* 718 | * Document-method: new 719 | * call-seq: Iconv.new(to, from, [options]) 720 | * 721 | * Creates new code converter from a coding-system designated with +from+ 722 | * to another one designated with +to+. 723 | * 724 | * === Parameters 725 | * 726 | * +to+:: encoding name for destination 727 | * +from+:: encoding name for source 728 | * +options+:: options for converter 729 | * 730 | * === Exceptions 731 | * 732 | * TypeError:: if +to+ or +from+ aren't String 733 | * InvalidEncoding:: if designated converter couldn't find out 734 | * SystemCallError:: if iconv_open(3) fails 735 | */ 736 | static VALUE 737 | iconv_initialize(int argc, VALUE *argv, VALUE self) 738 | { 739 | VALUE to, from, options; 740 | struct rb_iconv_opt_t opt; 741 | int idx; 742 | 743 | rb_scan_args(argc, argv, "21", &to, &from, &options); 744 | get_iconv_opt(&opt, options); 745 | iconv_free(check_iconv(self)); 746 | DATA_PTR(self) = NULL; 747 | DATA_PTR(self) = (void *)ICONV2VALUE(iconv_create(to, from, &opt, &idx)); 748 | #ifdef HAVE_RUBY_ENCODING_H 749 | ICONV_ENCODING_SET(self, idx); 750 | #endif 751 | return self; 752 | } 753 | 754 | /* 755 | * Document-method: open 756 | * call-seq: Iconv.open(to, from) { |iconv| ... } 757 | * 758 | * Equivalent to Iconv.new except that when it is called with a block, it 759 | * yields with the new instance and closes it, and returns the result which 760 | * returned from the block. 761 | */ 762 | static VALUE 763 | iconv_s_open(int argc, VALUE *argv, VALUE self) 764 | { 765 | VALUE to, from, options, cd; 766 | struct rb_iconv_opt_t opt; 767 | int idx; 768 | 769 | rb_scan_args(argc, argv, "21", &to, &from, &options); 770 | get_iconv_opt(&opt, options); 771 | cd = ICONV2VALUE(iconv_create(to, from, &opt, &idx)); 772 | 773 | self = Data_Wrap_Struct(self, NULL, ICONV_FREE, (void *)cd); 774 | #ifdef HAVE_RUBY_ENCODING_H 775 | if (idx >= 0) ICONV_ENCODING_SET(self, idx); 776 | #endif 777 | 778 | if (rb_block_given_p()) { 779 | return rb_ensure(rb_yield, self, iconv_finish, self); 780 | } 781 | else { 782 | return self; 783 | } 784 | } 785 | 786 | static VALUE 787 | iconv_s_convert(VALUE self) 788 | { 789 | struct iconv_env_t* env = (struct iconv_env_t*)self; 790 | VALUE last = 0; 791 | 792 | for (; env->argc > 0; --env->argc, ++env->argv) { 793 | VALUE s = iconv_convert(env->cd, last = *(env->argv), 794 | 0, -1, env->toidx, env); 795 | env->append(env->ret, s); 796 | } 797 | 798 | if (!NIL_P(last)) { 799 | VALUE s = iconv_convert(env->cd, Qnil, 0, 0, env->toidx, env); 800 | if (RSTRING_LEN(s)) 801 | env->append(env->ret, s); 802 | } 803 | 804 | return env->ret; 805 | } 806 | 807 | /* 808 | * Document-method: Iconv::iconv 809 | * call-seq: Iconv.iconv(to, from, *strs) 810 | * 811 | * Shorthand for 812 | * Iconv.open(to, from) { |cd| 813 | * (strs + [nil]).collect { |s| cd.iconv(s) } 814 | * } 815 | * 816 | * === Parameters 817 | * 818 | * to, from:: see Iconv.new 819 | * strs:: strings to be converted 820 | * 821 | * === Exceptions 822 | * 823 | * Exceptions thrown by Iconv.new, Iconv.open and Iconv#iconv. 824 | */ 825 | static VALUE 826 | iconv_s_iconv(int argc, VALUE *argv, VALUE self) 827 | { 828 | struct iconv_env_t arg; 829 | 830 | if (argc < 2) /* needs `to' and `from' arguments at least */ 831 | rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, 2); 832 | 833 | arg.argc = argc -= 2; 834 | arg.argv = argv + 2; 835 | arg.append = rb_ary_push; 836 | arg.ret = rb_ary_new2(argc); 837 | arg.cd = iconv_create(argv[0], argv[1], NULL, &arg.toidx); 838 | return rb_ensure(iconv_s_convert, (VALUE)&arg, iconv_free, ICONV2VALUE(arg.cd)); 839 | } 840 | 841 | /* 842 | * Document-method: Iconv::conv 843 | * call-seq: Iconv.conv(to, from, str) 844 | * 845 | * Shorthand for 846 | * Iconv.iconv(to, from, str).join 847 | * See Iconv.iconv. 848 | */ 849 | static VALUE 850 | iconv_s_conv(VALUE self, VALUE to, VALUE from, VALUE str) 851 | { 852 | struct iconv_env_t arg; 853 | 854 | arg.argc = 1; 855 | arg.argv = &str; 856 | arg.append = rb_str_append; 857 | arg.ret = rb_str_new(0, 0); 858 | arg.cd = iconv_create(to, from, NULL, &arg.toidx); 859 | return rb_ensure(iconv_s_convert, (VALUE)&arg, iconv_free, ICONV2VALUE(arg.cd)); 860 | } 861 | 862 | /* 863 | * Document-method: list 864 | * call-seq: Iconv.list {|*aliases| ... } 865 | * 866 | * Iterates each alias sets. 867 | */ 868 | 869 | #ifdef HAVE_ICONVLIST 870 | struct iconv_name_list 871 | { 872 | unsigned int namescount; 873 | const char *const *names; 874 | VALUE array; 875 | }; 876 | 877 | static VALUE 878 | list_iconv_i(VALUE ptr) 879 | { 880 | struct iconv_name_list *p = (struct iconv_name_list *)ptr; 881 | unsigned int i, namescount = p->namescount; 882 | const char *const *names = p->names; 883 | VALUE ary = rb_ary_new2(namescount); 884 | 885 | for (i = 0; i < namescount; i++) { 886 | rb_ary_push(ary, rb_str_new2(names[i])); 887 | } 888 | if (p->array) { 889 | return rb_ary_push(p->array, ary); 890 | } 891 | return rb_yield(ary); 892 | } 893 | 894 | static int 895 | list_iconv(unsigned int namescount, const char *const *names, void *data) 896 | { 897 | int *state = data; 898 | struct iconv_name_list list; 899 | 900 | list.namescount = namescount; 901 | list.names = names; 902 | list.array = ((VALUE *)data)[1]; 903 | rb_protect(list_iconv_i, (VALUE)&list, state); 904 | return *state; 905 | } 906 | #endif 907 | 908 | #if defined(HAVE_ICONVLIST) || defined(HAVE___ICONV_FREE_LIST) 909 | static VALUE 910 | iconv_s_list(VALUE klass) 911 | { 912 | #ifdef HAVE_ICONVLIST 913 | int state; 914 | VALUE args[2]; 915 | 916 | args[1] = rb_block_given_p() ? 0 : rb_ary_new(); 917 | iconvlist(list_iconv, args); 918 | state = (int)args[0]; 919 | if (state) rb_jump_tag(state); 920 | if (args[1]) return args[1]; 921 | #elif defined(HAVE___ICONV_FREE_LIST) 922 | char **list; 923 | size_t sz, i; 924 | VALUE ary; 925 | 926 | if (__iconv_get_list(&list, &sz)) return Qnil; 927 | 928 | ary = rb_ary_new2(sz); 929 | for (i = 0; i < sz; i++) { 930 | rb_ary_push(ary, rb_str_new2(list[i])); 931 | } 932 | __iconv_free_list(list, sz); 933 | 934 | if (!rb_block_given_p()) 935 | return ary; 936 | for (i = 0; i < RARRAY_LEN(ary); i++) { 937 | rb_yield(RARRAY_PTR(ary)[i]); 938 | } 939 | #endif 940 | return Qnil; 941 | } 942 | #else 943 | #define iconv_s_list rb_f_notimplement 944 | #endif 945 | 946 | /* 947 | * Document-method: close 948 | * 949 | * Finishes conversion. 950 | * 951 | * After calling this, calling Iconv#iconv will cause an exception, but 952 | * multiple calls of #close are guaranteed to end successfully. 953 | * 954 | * Returns a string containing the byte sequence to change the output buffer to 955 | * its initial shift state. 956 | */ 957 | static VALUE 958 | iconv_init_state(VALUE self) 959 | { 960 | iconv_t cd = VALUE2ICONV((VALUE)DATA_PTR(self)); 961 | DATA_PTR(self) = NULL; 962 | return iconv_convert(cd, Qnil, 0, 0, ICONV_ENCODING_GET(self), NULL); 963 | } 964 | 965 | static VALUE 966 | iconv_finish(VALUE self) 967 | { 968 | VALUE cd = check_iconv(self); 969 | 970 | if (!cd) return Qnil; 971 | return rb_ensure(iconv_init_state, self, iconv_free, cd); 972 | } 973 | 974 | /* 975 | * Document-method: Iconv#iconv 976 | * call-seq: iconv(str, start=0, length=-1) 977 | * 978 | * Converts string and returns the result. 979 | * * If +str+ is a String, converts str[start, length] and returns the converted string. 980 | * * If +str+ is +nil+, places converter itself into initial shift state and 981 | * just returns a string containing the byte sequence to change the output 982 | * buffer to its initial shift state. 983 | * * Otherwise, raises an exception. 984 | * 985 | * === Parameters 986 | * 987 | * str:: string to be converted, or nil 988 | * start:: starting offset 989 | * length:: conversion length; nil or -1 means whole the string from start 990 | * 991 | * === Exceptions 992 | * 993 | * * IconvIllegalSequence 994 | * * IconvInvalidCharacter 995 | * * IconvOutOfRange 996 | * 997 | * === Examples 998 | * 999 | * See the Iconv documentation. 1000 | */ 1001 | static VALUE 1002 | iconv_iconv(int argc, VALUE *argv, VALUE self) 1003 | { 1004 | VALUE str, n1, n2; 1005 | VALUE cd = check_iconv(self); 1006 | long start = 0, length = 0, slen = 0; 1007 | 1008 | rb_scan_args(argc, argv, "12", &str, &n1, &n2); 1009 | if (!NIL_P(str)) { 1010 | #ifdef HAVE_RUBY_ENCODING_H 1011 | VALUE n = rb_str_length(StringValue(str)); 1012 | slen = NUM2LONG(n); 1013 | #else 1014 | slen = RSTRING_LEN(StringValue(str)); 1015 | #endif 1016 | } 1017 | if (argc != 2 || !RTEST(rb_range_beg_len(n1, &start, &length, slen, 0))) { 1018 | if (NIL_P(n1) || ((start = NUM2LONG(n1)) < 0 ? (start += slen) >= 0 : start < slen)) { 1019 | length = NIL_P(n2) ? -1 : NUM2LONG(n2); 1020 | } 1021 | } 1022 | if (start > 0 || length > 0) { 1023 | #ifdef HAVE_RUBY_ENCODING_H 1024 | const char *s = RSTRING_PTR(str), *e = s + RSTRING_LEN(str); 1025 | const char *ps = s; 1026 | rb_encoding *enc = rb_enc_get(str); 1027 | if (start > 0) { 1028 | start = (ps = rb_enc_nth(s, e, start, enc)) - s; 1029 | } 1030 | if (length > 0) { 1031 | length = rb_enc_nth(ps, e, length, enc) - ps; 1032 | } 1033 | #else 1034 | if (start > slen) { 1035 | start = slen; 1036 | } 1037 | if (length > slen - start) { 1038 | length = slen - start; 1039 | } 1040 | #endif 1041 | } 1042 | 1043 | return iconv_convert(VALUE2ICONV(cd), str, start, length, ICONV_ENCODING_GET(self), NULL); 1044 | } 1045 | 1046 | /* 1047 | * Document-method: conv 1048 | * call-seq: conv(str...) 1049 | * 1050 | * Equivalent to 1051 | * 1052 | * iconv(nil, str..., nil).join 1053 | */ 1054 | static VALUE 1055 | iconv_conv(int argc, VALUE *argv, VALUE self) 1056 | { 1057 | iconv_t cd = VALUE2ICONV(check_iconv(self)); 1058 | VALUE str, s; 1059 | int toidx = ICONV_ENCODING_GET(self); 1060 | 1061 | str = iconv_convert(cd, Qnil, 0, 0, toidx, NULL); 1062 | if (argc > 0) { 1063 | do { 1064 | s = iconv_convert(cd, *argv++, 0, -1, toidx, NULL); 1065 | if (RSTRING_LEN(s)) 1066 | rb_str_buf_append(str, s); 1067 | } while (--argc); 1068 | s = iconv_convert(cd, Qnil, 0, 0, toidx, NULL); 1069 | if (RSTRING_LEN(s)) 1070 | rb_str_buf_append(str, s); 1071 | } 1072 | 1073 | return str; 1074 | } 1075 | 1076 | #ifdef ICONV_TRIVIALP 1077 | /* 1078 | * Document-method: trivial? 1079 | * call-seq: trivial? 1080 | * 1081 | * Returns trivial flag. 1082 | */ 1083 | static VALUE 1084 | iconv_trivialp(VALUE self) 1085 | { 1086 | int trivial = 0; 1087 | iconv_ctl(self, ICONV_TRIVIALP, trivial); 1088 | if (trivial) return Qtrue; 1089 | return Qfalse; 1090 | } 1091 | #else 1092 | #define iconv_trivialp rb_f_notimplement 1093 | #endif 1094 | 1095 | #ifdef ICONV_GET_TRANSLITERATE 1096 | /* 1097 | * Document-method: transliterate? 1098 | * call-seq: transliterate? 1099 | * 1100 | * Returns transliterate flag. 1101 | */ 1102 | static VALUE 1103 | iconv_get_transliterate(VALUE self) 1104 | { 1105 | int trans = 0; 1106 | iconv_ctl(self, ICONV_GET_TRANSLITERATE, trans); 1107 | if (trans) return Qtrue; 1108 | return Qfalse; 1109 | } 1110 | #else 1111 | #define iconv_get_transliterate rb_f_notimplement 1112 | #endif 1113 | 1114 | #ifdef ICONV_SET_TRANSLITERATE 1115 | /* 1116 | * Document-method: transliterate= 1117 | * call-seq: cd.transliterate = flag 1118 | * 1119 | * Sets transliterate flag. 1120 | */ 1121 | static VALUE 1122 | iconv_set_transliterate(VALUE self, VALUE transliterate) 1123 | { 1124 | int trans = RTEST(transliterate); 1125 | iconv_ctl(self, ICONV_SET_TRANSLITERATE, trans); 1126 | return self; 1127 | } 1128 | #else 1129 | #define iconv_set_transliterate rb_f_notimplement 1130 | #endif 1131 | 1132 | #ifdef ICONV_GET_DISCARD_ILSEQ 1133 | /* 1134 | * Document-method: discard_ilseq? 1135 | * call-seq: discard_ilseq? 1136 | * 1137 | * Returns discard_ilseq flag. 1138 | */ 1139 | static VALUE 1140 | iconv_get_discard_ilseq(VALUE self) 1141 | { 1142 | int dis = 0; 1143 | iconv_ctl(self, ICONV_GET_DISCARD_ILSEQ, dis); 1144 | if (dis) return Qtrue; 1145 | return Qfalse; 1146 | } 1147 | #else 1148 | #define iconv_get_discard_ilseq rb_f_notimplement 1149 | #endif 1150 | 1151 | #ifdef ICONV_SET_DISCARD_ILSEQ 1152 | /* 1153 | * Document-method: discard_ilseq= 1154 | * call-seq: cd.discard_ilseq = flag 1155 | * 1156 | * Sets discard_ilseq flag. 1157 | */ 1158 | static VALUE 1159 | iconv_set_discard_ilseq(VALUE self, VALUE discard_ilseq) 1160 | { 1161 | int dis = RTEST(discard_ilseq); 1162 | iconv_ctl(self, ICONV_SET_DISCARD_ILSEQ, dis); 1163 | return self; 1164 | } 1165 | #else 1166 | #define iconv_set_discard_ilseq rb_f_notimplement 1167 | #endif 1168 | 1169 | /* 1170 | * Document-method: ctlmethods 1171 | * call-seq: Iconv.ctlmethods => array 1172 | * 1173 | * Returns available iconvctl() method list. 1174 | */ 1175 | static VALUE 1176 | iconv_s_ctlmethods(VALUE klass) 1177 | { 1178 | VALUE ary = rb_ary_new(); 1179 | #ifdef ICONV_TRIVIALP 1180 | rb_ary_push(ary, ID2SYM(rb_intern("trivial?"))); 1181 | #endif 1182 | #ifdef ICONV_GET_TRANSLITERATE 1183 | rb_ary_push(ary, ID2SYM(rb_intern("transliterate?"))); 1184 | #endif 1185 | #ifdef ICONV_SET_TRANSLITERATE 1186 | rb_ary_push(ary, ID2SYM(rb_intern("transliterate="))); 1187 | #endif 1188 | #ifdef ICONV_GET_DISCARD_ILSEQ 1189 | rb_ary_push(ary, ID2SYM(rb_intern("discard_ilseq?"))); 1190 | #endif 1191 | #ifdef ICONV_SET_DISCARD_ILSEQ 1192 | rb_ary_push(ary, ID2SYM(rb_intern("discard_ilseq="))); 1193 | #endif 1194 | return ary; 1195 | } 1196 | 1197 | /* 1198 | * Document-class: Iconv::Failure 1199 | * 1200 | * Base attributes for Iconv exceptions. 1201 | */ 1202 | 1203 | /* 1204 | * Document-method: success 1205 | * call-seq: success 1206 | * 1207 | * Returns string(s) translated successfully until the exception occurred. 1208 | * * In the case of failure occurred within Iconv.iconv, returned 1209 | * value is an array of strings translated successfully preceding 1210 | * failure and the last element is string on the way. 1211 | */ 1212 | static VALUE 1213 | iconv_failure_success(VALUE self) 1214 | { 1215 | return rb_attr_get(self, rb_success); 1216 | } 1217 | 1218 | /* 1219 | * Document-method: failed 1220 | * call-seq: failed 1221 | * 1222 | * Returns substring of the original string passed to Iconv that starts at the 1223 | * character caused the exception. 1224 | */ 1225 | static VALUE 1226 | iconv_failure_failed(VALUE self) 1227 | { 1228 | return rb_attr_get(self, rb_failed); 1229 | } 1230 | 1231 | /* 1232 | * Document-method: inspect 1233 | * call-seq: inspect 1234 | * 1235 | * Returns inspected string like as: #<_class_: _success_, _failed_> 1236 | */ 1237 | static VALUE 1238 | iconv_failure_inspect(VALUE self) 1239 | { 1240 | const char *cname = rb_class2name(CLASS_OF(self)); 1241 | VALUE success = rb_attr_get(self, rb_success); 1242 | VALUE failed = rb_attr_get(self, rb_failed); 1243 | VALUE str = rb_str_buf_cat2(rb_str_new2("#<"), cname); 1244 | str = rb_str_buf_cat(str, ": ", 2); 1245 | str = rb_str_buf_append(str, rb_inspect(success)); 1246 | str = rb_str_buf_cat(str, ", ", 2); 1247 | str = rb_str_buf_append(str, rb_inspect(failed)); 1248 | return rb_str_buf_cat(str, ">", 1); 1249 | } 1250 | 1251 | /* 1252 | * Document-class: Iconv::InvalidEncoding 1253 | * 1254 | * Requested coding-system is not available on this system. 1255 | */ 1256 | 1257 | /* 1258 | * Document-class: Iconv::IllegalSequence 1259 | * 1260 | * Input conversion stopped due to an input byte that does not belong to 1261 | * the input codeset, or the output codeset does not contain the 1262 | * character. 1263 | */ 1264 | 1265 | /* 1266 | * Document-class: Iconv::InvalidCharacter 1267 | * 1268 | * Input conversion stopped due to an incomplete character or shift 1269 | * sequence at the end of the input buffer. 1270 | */ 1271 | 1272 | /* 1273 | * Document-class: Iconv::OutOfRange 1274 | * 1275 | * Iconv library internal error. Must not occur. 1276 | */ 1277 | 1278 | /* 1279 | * Document-class: Iconv::BrokenLibrary 1280 | * 1281 | * Detected a bug of underlying iconv(3) libray. 1282 | * * returns an error without setting errno properly 1283 | */ 1284 | 1285 | void 1286 | Init_iconv(void) 1287 | { 1288 | VALUE rb_cIconv = rb_define_class("Iconv", rb_cObject); 1289 | 1290 | rb_define_alloc_func(rb_cIconv, iconv_s_allocate); 1291 | rb_define_singleton_method(rb_cIconv, "open", iconv_s_open, -1); 1292 | rb_define_singleton_method(rb_cIconv, "iconv", iconv_s_iconv, -1); 1293 | rb_define_singleton_method(rb_cIconv, "conv", iconv_s_conv, 3); 1294 | rb_define_singleton_method(rb_cIconv, "list", iconv_s_list, 0); 1295 | rb_define_singleton_method(rb_cIconv, "ctlmethods", iconv_s_ctlmethods, 0); 1296 | rb_define_method(rb_cIconv, "initialize", iconv_initialize, -1); 1297 | rb_define_method(rb_cIconv, "close", iconv_finish, 0); 1298 | rb_define_method(rb_cIconv, "iconv", iconv_iconv, -1); 1299 | rb_define_method(rb_cIconv, "conv", iconv_conv, -1); 1300 | rb_define_method(rb_cIconv, "trivial?", iconv_trivialp, 0); 1301 | rb_define_method(rb_cIconv, "transliterate?", iconv_get_transliterate, 0); 1302 | rb_define_method(rb_cIconv, "transliterate=", iconv_set_transliterate, 1); 1303 | rb_define_method(rb_cIconv, "discard_ilseq?", iconv_get_discard_ilseq, 0); 1304 | rb_define_method(rb_cIconv, "discard_ilseq=", iconv_set_discard_ilseq, 1); 1305 | 1306 | rb_eIconvFailure = rb_define_module_under(rb_cIconv, "Failure"); 1307 | rb_define_method(rb_eIconvFailure, "initialize", iconv_failure_initialize, 3); 1308 | rb_define_method(rb_eIconvFailure, "success", iconv_failure_success, 0); 1309 | rb_define_method(rb_eIconvFailure, "failed", iconv_failure_failed, 0); 1310 | rb_define_method(rb_eIconvFailure, "inspect", iconv_failure_inspect, 0); 1311 | 1312 | rb_eIconvInvalidEncoding = rb_define_class_under(rb_cIconv, "InvalidEncoding", rb_eArgError); 1313 | rb_eIconvIllegalSeq = rb_define_class_under(rb_cIconv, "IllegalSequence", rb_eArgError); 1314 | rb_eIconvInvalidChar = rb_define_class_under(rb_cIconv, "InvalidCharacter", rb_eArgError); 1315 | rb_eIconvOutOfRange = rb_define_class_under(rb_cIconv, "OutOfRange", rb_eRuntimeError); 1316 | rb_eIconvBrokenLibrary = rb_define_class_under(rb_cIconv, "BrokenLibrary", rb_eRuntimeError); 1317 | rb_include_module(rb_eIconvInvalidEncoding, rb_eIconvFailure); 1318 | rb_include_module(rb_eIconvIllegalSeq, rb_eIconvFailure); 1319 | rb_include_module(rb_eIconvInvalidChar, rb_eIconvFailure); 1320 | rb_include_module(rb_eIconvOutOfRange, rb_eIconvFailure); 1321 | rb_include_module(rb_eIconvBrokenLibrary, rb_eIconvFailure); 1322 | 1323 | rb_success = rb_intern("success"); 1324 | rb_failed = rb_intern("failed"); 1325 | id_transliterate = rb_intern("transliterate"); 1326 | id_discard_ilseq = rb_intern("discard_ilseq"); 1327 | id_encindex = rb_intern("encindex"); 1328 | 1329 | rb_gc_register_address(&charset_map); 1330 | charset_map = rb_hash_new(); 1331 | rb_define_singleton_method(rb_cIconv, "charset_map", charset_map_get, 0); 1332 | } 1333 | 1334 | --------------------------------------------------------------------------------