ffi-chm 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.txt CHANGED
@@ -9,12 +9,15 @@ Ruby bindings for the libchm via FFI.
9
9
  == FEATURES/PROBLEMS:
10
10
 
11
11
  * Provides basic access to the CHM files.
12
- * Currently developed on MRI 1.8 / 1.9
12
+ * Some functionalities to access to internal structure.
13
+ * Currently developed on MRI 1.8.7 / 1.9.2
13
14
 
14
15
  == SYNOPSIS:
15
16
 
16
17
  FFI::Chm::ChmFile.new("reference-manual.chm") do |chm|
17
18
  chm.enumerate(:normal, :files).map{|ui| ui[:path]}
19
+
20
+ puts chm.title
18
21
  end
19
22
 
20
23
  == REQUIREMENTS:
@@ -0,0 +1,39 @@
1
+ require 'nokogiri'
2
+ require 'ffi-chm/com8ble'
3
+
4
+ module FFI::Chm::ChmFile::Aux
5
+ include FFI::Chm
6
+
7
+ def system
8
+ @system ||= Struct::System.new.read self.retrieve_object("/#SYSTEM")
9
+ end
10
+
11
+ def encoding
12
+ @encoding ||= self.system.encoding
13
+ end
14
+
15
+ def title
16
+ @title ||= self.system.record(3).data.title.com8ble.force_encoding(encoding).encode("UTF-8")
17
+ end
18
+
19
+ def contents(cache=true)
20
+ return @contents if @contents
21
+ xml = self.retrieve_object("/#{self.system.record(6).data.compiled_file}.hhc").com8ble.force_encoding(encoding).encode("UTF-8")
22
+ doc = Struct::HHXDocument.new(Struct::HHC::Entry)
23
+ Nokogiri::HTML::SAX::Parser.new(doc).parse(xml)
24
+
25
+ doc.root.first.tap do |v|
26
+ @contents = v if cache
27
+ end
28
+ end
29
+
30
+ def index(cache=true)
31
+ return @index if @index
32
+ xml = self.retrieve_object("/#{self.system.record(6).data.compiled_file}.hhk").com8ble.force_encoding(encoding).encode("UTF-8")
33
+ doc = Struct::HHXDocument.new(Struct::HHK::Entry)
34
+ Nokogiri::HTML::SAX::Parser.new(doc).parse(xml)
35
+ doc.root.flatten.tap do |v|
36
+ @index = v if cache
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,102 @@
1
+ module FFI::Chm
2
+ class ChmFile
3
+ require 'ffi-chm/chm_file/aux'
4
+ include Aux
5
+
6
+ attr_reader :path
7
+
8
+ def initialize(path, &block)
9
+ @path = path
10
+ self.open &block if block_given?
11
+ end
12
+
13
+ def open(path=@path, &block)
14
+ @path = path
15
+ @h = API.chm_open @path
16
+ raise ChmError, "Not exists?" if @h.null?
17
+ if block_given?
18
+ begin
19
+ yield self
20
+ ensure
21
+ self.close
22
+ end
23
+ else
24
+ self
25
+ end
26
+ end
27
+
28
+ def close
29
+ API.chm_close @h unless @h.null?
30
+ end
31
+
32
+ def set_param(param, value)
33
+ API.chm_set_param @h, param, value
34
+ end
35
+
36
+ def resolve_object(name)
37
+ ui = UnitInfo.new
38
+ case API.chm_resolve_object @h, name, ui
39
+ when Const::RESOLVE_SUCCESS
40
+ ui
41
+ when Const::RESOLVE_FAILURE
42
+ raise ResolveError
43
+ end
44
+ end
45
+
46
+ def retrieve_object(ui)
47
+ if UnitInfo === ui
48
+ buf = FFI::Buffer.new ui[:length]
49
+ API.chm_retrieve_object @h, ui, buf, 0, ui[:length]
50
+ buf.read_bytes buf.size
51
+ else
52
+ retrieve_object resolve_object(ui)
53
+ end
54
+ rescue ResolveError
55
+ raise RetrieveError, ui
56
+ end
57
+
58
+ require 'enumerator'
59
+ def enumerate(*what, &block)
60
+ if block_given?
61
+ API.chm_enumerate @h, to_flags(what), enum_func(&block), nil
62
+ self
63
+ else
64
+ (Enumerable::Enumerator rescue Enumerator).new self, :enumerate, to_flags(what)
65
+ end
66
+ end
67
+
68
+ def enumerate_dir(prefix, *what, &block)
69
+ if block_given?
70
+ API.chm_enumerate_dir @h, prefix, to_flags(what), enum_func(&block), nil
71
+ self
72
+ else
73
+ (Enumerable::Enumerator rescue Enumerator).new self, :enumerate_dir, prefix, to_flags(what)
74
+ end
75
+ end
76
+
77
+ private
78
+ def enum_func(&block)
79
+ lambda do |h, ui, _|
80
+ begin
81
+ if block.call(UnitInfo.new(ui).clone) == :break
82
+ Const::ENUMERATOR_SUCCESS
83
+ else
84
+ Const::ENUMERATOR_CONTINUE
85
+ end
86
+ rescue
87
+ Const::ENUMERATOR_FAILURE
88
+ end
89
+ end
90
+ end
91
+
92
+ def to_flags(what)
93
+ if what.size == 1 && Integer === what.first
94
+ what.first
95
+ else
96
+ what.inject(0) do |r, sym|
97
+ r | (Const::ENUMERATE_FLAGS[sym] || 0)
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,37 @@
1
+ module Com8ble
2
+ module String
3
+ require 'iconv'
4
+
5
+ def force_encoding(encoding)
6
+ @encoding = _encoding(encoding)
7
+ self
8
+ end
9
+
10
+ def encoding
11
+ @encoding
12
+ end
13
+
14
+ def encode(encoding)
15
+ r = Iconv.conv(_encoding(encoding).name, @encoding.name, self)
16
+ r.extend String
17
+ r.force_encoding encoding
18
+ r
19
+ end
20
+
21
+ private
22
+ def _encoding(encoding)
23
+ if ::String === encoding
24
+ Struct.new(:name).new(encoding)
25
+ else
26
+ encoding
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ class String
33
+ def com8ble
34
+ self.extend Com8ble::String unless self.respond_to? :force_encoding
35
+ self
36
+ end
37
+ end
@@ -0,0 +1,4 @@
1
+ module FFI::Chm::Struct::HHC
2
+ class Entry < Struct.new(:Name, :Type, :Local, :URL, :FrameName, :WindowName, :Comment, :Merge, :ImageNumber, :New)
3
+ end
4
+ end
@@ -0,0 +1,7 @@
1
+ module FFI::Chm::Struct::HHK
2
+ class Entry < Struct.new(:Name, :Type, :Local, :URL, :FrameName, :WindowName, :Comment, :Merge, :'See Also')
3
+ def SeeAlso
4
+ self["See Also"]
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,41 @@
1
+ module FFI::Chm::Struct
2
+ class HHXDocument < Nokogiri::XML::SAX::Document
3
+ # ul - li - object - param
4
+ attr_reader :root
5
+
6
+ def initialize(entry_klass)
7
+ @klass = entry_klass
8
+ end
9
+
10
+ def start_document
11
+ @root = []
12
+ @heads = []
13
+ @heads << @root
14
+ end
15
+
16
+ def start_element(name, attributes = [])
17
+ case name
18
+ when "param"
19
+ return unless @entry
20
+ attr = {}
21
+ attributes.each_slice(2){|k, v| attr[k.intern] = v}
22
+ @entry[attr[:name].intern] = attr[:value]
23
+ when "li"
24
+ @entry = @klass.new
25
+ when "ul"
26
+ head = []
27
+ @heads.last << head
28
+ @heads << head
29
+ end
30
+ end
31
+
32
+ def end_element(name, *attributes)
33
+ case name
34
+ when "object" # li results broken structure.
35
+ @heads.last << @entry if @entry
36
+ when "ul"
37
+ @heads.pop
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,3 @@
1
+ class FFI::Chm::Struct::System::Code3 < BinData::Record
2
+ stringz :title
3
+ end
@@ -0,0 +1,11 @@
1
+ class FFI::Chm::Struct::System::Code4 < BinData::Record
2
+ endian :little
3
+
4
+ uint32 :lcid
5
+ uint32 :use_dbcs
6
+ uint32 :fulltext_enabled
7
+ uint32 :has_klinks
8
+ uint32 :has_alinks
9
+ uint64 :timestamp
10
+ uint64 :unknown
11
+ end
@@ -0,0 +1,3 @@
1
+ class FFI::Chm::Struct::System::Code6 < BinData::Record
2
+ stringz :compiled_file
3
+ end
@@ -0,0 +1,17 @@
1
+ class FFI::Chm::Struct::System::Record < BinData::Record
2
+ require 'ffi-chm/struct/system/code3'
3
+ require 'ffi-chm/struct/system/code4'
4
+ require 'ffi-chm/struct/system/code6'
5
+
6
+ endian :little
7
+
8
+ uint16 :code
9
+ uint16 :len
10
+
11
+ choice :data, :selection => :code do
12
+ code3 3
13
+ code4 4
14
+ code6 6
15
+ string :default, :read_length => :len
16
+ end
17
+ end
@@ -0,0 +1,23 @@
1
+ class FFI::Chm::Struct::System < BinData::Record
2
+ require 'ffi-chm/struct/system/record'
3
+
4
+ endian :little
5
+
6
+ uint32 :version
7
+ array :records, :type => :record, :read_until => :eof
8
+
9
+
10
+ def record(code)
11
+ @memo ||= Hash[*records.map{|v|[v.code.to_i, v]}.flatten]
12
+ @memo[code]
13
+ end
14
+
15
+ def encoding
16
+ case self.record(4).data.lcid.to_i
17
+ when 1041
18
+ "CP932"
19
+ else
20
+ "UTF-8"
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,8 @@
1
+ require 'bindata'
2
+
3
+ module FFI::Chm::Struct
4
+ autoload :System, 'ffi-chm/struct/system'
5
+ autoload :HHK, 'ffi-chm/struct/hhk/entry'
6
+ autoload :HHC, 'ffi-chm/struct/hhc/entry'
7
+ autoload :HHXDocument, 'ffi-chm/struct/hhx_document'
8
+ end
@@ -0,0 +1,28 @@
1
+ module FFI::Chm
2
+ class UnitInfo < FFI::Struct
3
+ layout :start, :uint64,
4
+ :length, :uint64,
5
+ :space, :int,
6
+ :flags, :int,
7
+ :path, [:char, Const::MAX_PATHLEN + 1]
8
+
9
+ def dir?
10
+ flag? :dirs
11
+ end
12
+
13
+ def file?
14
+ flag? :files
15
+ end
16
+
17
+ %w(normal special meta).each do |name|
18
+ define_method("#{name}?") do
19
+ flag? name.intern
20
+ end
21
+ end
22
+
23
+ private
24
+ def flag?(flag)
25
+ !!(self[:flags] & Const::ENUMERATE_FLAGS[flag]).nonzero?
26
+ end
27
+ end
28
+ end
data/lib/ffi-chm.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  module FFI
2
2
  module Chm
3
- VERSION = '0.1.0'
3
+ VERSION = '0.2.0'
4
4
  end
5
5
  end
6
6
 
@@ -8,125 +8,14 @@ require 'ffi-chm/const'
8
8
  require 'ffi-chm/api'
9
9
 
10
10
  module FFI::Chm
11
+ autoload :Struct, 'ffi-chm/struct'
12
+ autoload :ChmFile, 'ffi-chm/chm_file'
13
+ autoload :UnitInfo, 'ffi-chm/unit_info'
14
+
11
15
  include API
12
16
  include Const
13
17
 
14
18
  class ChmError < StandardError;end
15
19
  class ResolveError < ChmError;end
16
20
  class RetrieveError < ChmError;end
17
-
18
- class ChmFile
19
- def initialize(fn)
20
- @h = API.chm_open fn
21
- raise ChmError, "Not exists?" if @h.null?
22
- if block_given?
23
- begin
24
- yield self
25
- ensure
26
- self.close
27
- end
28
- end
29
- end
30
-
31
- def close
32
- API.chm_close @h unless @h.null?
33
- end
34
-
35
- def set_param(param, value)
36
- API.chm_set_param @h, param, value
37
- end
38
-
39
- def resolve_object(name)
40
- ui = UnitInfo.new
41
- case API.chm_resolve_object @h, name, ui
42
- when Const::RESOLVE_SUCCESS
43
- ui
44
- when Const::RESOLVE_FAILURE
45
- raise ResolveError
46
- end
47
- end
48
-
49
- def retrieve_object(ui)
50
- if UnitInfo === ui
51
- buf = FFI::Buffer.new ui[:length]
52
- API.chm_retrieve_object @h, ui, buf, 0, ui[:length]
53
- buf.read_bytes buf.size
54
- else
55
- retrieve_object resolve_object(ui)
56
- end
57
- rescue ResolveError
58
- raise RetrieveError, ui
59
- end
60
-
61
- require 'enumerator'
62
- def enumerate(*what, &block)
63
- if block_given?
64
- API.chm_enumerate @h, to_flags(what), enum_func(&block), nil
65
- self
66
- else
67
- (Enumerable::Enumerator rescue Enumerator).new self, :enumerate, to_flags(what)
68
- end
69
- end
70
-
71
- def enumerate_dir(prefix, *what, &block)
72
- if block_given?
73
- API.chm_enumerate_dir @h, prefix, to_flags(what), enum_func(&block), nil
74
- self
75
- else
76
- (Enumerable::Enumerator rescue Enumerator).new self, :enumerate_dir, prefix, to_flags(what)
77
- end
78
- end
79
-
80
- private
81
- def enum_func(&block)
82
- lambda do |h, ui, _|
83
- begin
84
- if block.call(UnitInfo.new(ui).clone) == :break
85
- Const::ENUMERATOR_SUCCESS
86
- else
87
- Const::ENUMERATOR_CONTINUE
88
- end
89
- rescue
90
- Const::ENUMERATOR_FAILURE
91
- end
92
- end
93
- end
94
-
95
- def to_flags(what)
96
- if what.size == 1 && Integer === what.first
97
- what.first
98
- else
99
- what.inject(0) do |r, sym|
100
- r | (Const::ENUMERATE_FLAGS[sym] || 0)
101
- end
102
- end
103
- end
104
- end
105
-
106
- class UnitInfo < FFI::Struct
107
- layout :start, :uint64,
108
- :length, :uint64,
109
- :space, :int,
110
- :flags, :int,
111
- :path, [:char, Const::MAX_PATHLEN + 1]
112
-
113
- def dir?
114
- flag? :dirs
115
- end
116
-
117
- def file?
118
- flag? :files
119
- end
120
-
121
- %w(normal special meta).each do |name|
122
- define_method("#{name}?") do
123
- flag? name.intern
124
- end
125
- end
126
-
127
- private
128
- def flag?(flag)
129
- !!(self[:flags] & Const::ENUMERATE_FLAGS[flag]).nonzero?
130
- end
131
- end
132
21
  end
metadata CHANGED
@@ -1,102 +1,112 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: ffi-chm
3
- version: !ruby/object:Gem::Version
4
- hash: 27
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
5
  prerelease:
6
- segments:
7
- - 0
8
- - 1
9
- - 0
10
- version: 0.1.0
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - nanki
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2011-07-28 00:00:00 +09:00
19
- default_executable:
20
- dependencies:
21
- - !ruby/object:Gem::Dependency
12
+ date: 2011-08-04 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
22
15
  name: ffi
16
+ requirement: &70275895477860 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
23
  prerelease: false
24
- requirement: &id001 !ruby/object:Gem::Requirement
24
+ version_requirements: *70275895477860
25
+ - !ruby/object:Gem::Dependency
26
+ name: bindata
27
+ requirement: &70275895475840 !ruby/object:Gem::Requirement
25
28
  none: false
26
- requirements:
27
- - - ">="
28
- - !ruby/object:Gem::Version
29
- hash: 3
30
- segments:
31
- - 0
32
- version: "0"
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70275895475840
36
+ - !ruby/object:Gem::Dependency
37
+ name: nokogiri
38
+ requirement: &70275895474760 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
33
44
  type: :runtime
34
- version_requirements: *id001
35
- - !ruby/object:Gem::Dependency
36
- name: hoe
37
45
  prerelease: false
38
- requirement: &id002 !ruby/object:Gem::Requirement
46
+ version_requirements: *70275895474760
47
+ - !ruby/object:Gem::Dependency
48
+ name: hoe
49
+ requirement: &70275895473500 !ruby/object:Gem::Requirement
39
50
  none: false
40
- requirements:
51
+ requirements:
41
52
  - - ~>
42
- - !ruby/object:Gem::Version
43
- hash: 23
44
- segments:
45
- - 2
46
- - 10
47
- version: "2.10"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.10'
48
55
  type: :development
49
- version_requirements: *id002
56
+ prerelease: false
57
+ version_requirements: *70275895473500
50
58
  description: Ruby bindings for the libchm via FFI.
51
- email:
59
+ email:
52
60
  - nanki@dotswitch.net
53
61
  executables: []
54
-
55
62
  extensions: []
56
-
57
- extra_rdoc_files:
63
+ extra_rdoc_files:
58
64
  - README.txt
59
- files:
65
+ files:
60
66
  - README.txt
61
- - lib/ffi-chm.rb
62
67
  - lib/ffi-chm/api.rb
68
+ - lib/ffi-chm/chm_file/aux.rb
69
+ - lib/ffi-chm/chm_file.rb
70
+ - lib/ffi-chm/com8ble.rb
63
71
  - lib/ffi-chm/const.rb
72
+ - lib/ffi-chm/struct/hhc/entry.rb
73
+ - lib/ffi-chm/struct/hhk/entry.rb
74
+ - lib/ffi-chm/struct/hhx_document.rb
75
+ - lib/ffi-chm/struct/system/code3.rb
76
+ - lib/ffi-chm/struct/system/code4.rb
77
+ - lib/ffi-chm/struct/system/code6.rb
78
+ - lib/ffi-chm/struct/system/record.rb
79
+ - lib/ffi-chm/struct/system.rb
80
+ - lib/ffi-chm/struct.rb
81
+ - lib/ffi-chm/unit_info.rb
82
+ - lib/ffi-chm.rb
64
83
  - test/test_ffi_chm.rb
65
84
  - .gemtest
66
- has_rdoc: true
67
85
  homepage: http://github.com/nanki/ffi-chm
68
86
  licenses: []
69
-
70
87
  post_install_message:
71
- rdoc_options:
88
+ rdoc_options:
72
89
  - --main
73
90
  - README.txt
74
- require_paths:
91
+ require_paths:
75
92
  - lib
76
- required_ruby_version: !ruby/object:Gem::Requirement
93
+ required_ruby_version: !ruby/object:Gem::Requirement
77
94
  none: false
78
- requirements:
79
- - - ">="
80
- - !ruby/object:Gem::Version
81
- hash: 3
82
- segments:
83
- - 0
84
- version: "0"
85
- required_rubygems_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
100
  none: false
87
- requirements:
88
- - - ">="
89
- - !ruby/object:Gem::Version
90
- hash: 3
91
- segments:
92
- - 0
93
- version: "0"
101
+ requirements:
102
+ - - ! '>='
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
94
105
  requirements: []
95
-
96
106
  rubyforge_project: ffi-chm
97
- rubygems_version: 1.6.2
107
+ rubygems_version: 1.8.6
98
108
  signing_key:
99
109
  specification_version: 3
100
110
  summary: Ruby bindings for the libchm via FFI.
101
- test_files:
111
+ test_files:
102
112
  - test/test_ffi_chm.rb