bitclust-core 0.5.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.
Files changed (127) hide show
  1. data/ChangeLog +2907 -0
  2. data/Gemfile +7 -0
  3. data/README +21 -0
  4. data/Rakefile +20 -0
  5. data/bin/bitclust +14 -0
  6. data/bin/refe +36 -0
  7. data/bitclust-dev.gemspec +33 -0
  8. data/bitclust.gemspec +30 -0
  9. data/config.in +23 -0
  10. data/config.ru +48 -0
  11. data/config.ru.sample +31 -0
  12. data/data/bitclust/catalog/ja_JP.EUC-JP +78 -0
  13. data/data/bitclust/catalog/ja_JP.UTF-8 +78 -0
  14. data/data/bitclust/template.lillia/class +98 -0
  15. data/data/bitclust/template.lillia/class-index +28 -0
  16. data/data/bitclust/template.lillia/doc +48 -0
  17. data/data/bitclust/template.lillia/layout +19 -0
  18. data/data/bitclust/template.lillia/library +129 -0
  19. data/data/bitclust/template.lillia/library-index +32 -0
  20. data/data/bitclust/template.lillia/method +20 -0
  21. data/data/bitclust/template.lillia/rd_file +6 -0
  22. data/data/bitclust/template.offline/class +67 -0
  23. data/data/bitclust/template.offline/class-index +28 -0
  24. data/data/bitclust/template.offline/doc +13 -0
  25. data/data/bitclust/template.offline/function +22 -0
  26. data/data/bitclust/template.offline/function-index +24 -0
  27. data/data/bitclust/template.offline/layout +18 -0
  28. data/data/bitclust/template.offline/library +87 -0
  29. data/data/bitclust/template.offline/library-index +32 -0
  30. data/data/bitclust/template.offline/method +21 -0
  31. data/data/bitclust/template.offline/rd_file +6 -0
  32. data/data/bitclust/template/class +133 -0
  33. data/data/bitclust/template/class-index +30 -0
  34. data/data/bitclust/template/doc +14 -0
  35. data/data/bitclust/template/function +21 -0
  36. data/data/bitclust/template/function-index +25 -0
  37. data/data/bitclust/template/layout +19 -0
  38. data/data/bitclust/template/library +89 -0
  39. data/data/bitclust/template/library-index +35 -0
  40. data/data/bitclust/template/method +24 -0
  41. data/data/bitclust/template/opensearchdescription +10 -0
  42. data/data/bitclust/template/search +57 -0
  43. data/lib/bitclust.rb +9 -0
  44. data/lib/bitclust/app.rb +129 -0
  45. data/lib/bitclust/classentry.rb +425 -0
  46. data/lib/bitclust/compat.rb +39 -0
  47. data/lib/bitclust/completion.rb +531 -0
  48. data/lib/bitclust/crossrubyutils.rb +91 -0
  49. data/lib/bitclust/database.rb +181 -0
  50. data/lib/bitclust/docentry.rb +83 -0
  51. data/lib/bitclust/entry.rb +223 -0
  52. data/lib/bitclust/exception.rb +38 -0
  53. data/lib/bitclust/functiondatabase.rb +115 -0
  54. data/lib/bitclust/functionentry.rb +81 -0
  55. data/lib/bitclust/functionreferenceparser.rb +76 -0
  56. data/lib/bitclust/htmlutils.rb +80 -0
  57. data/lib/bitclust/interface.rb +87 -0
  58. data/lib/bitclust/libraryentry.rb +211 -0
  59. data/lib/bitclust/lineinput.rb +165 -0
  60. data/lib/bitclust/messagecatalog.rb +95 -0
  61. data/lib/bitclust/methoddatabase.rb +401 -0
  62. data/lib/bitclust/methodentry.rb +202 -0
  63. data/lib/bitclust/methodid.rb +209 -0
  64. data/lib/bitclust/methodsignature.rb +82 -0
  65. data/lib/bitclust/nameutils.rb +236 -0
  66. data/lib/bitclust/parseutils.rb +60 -0
  67. data/lib/bitclust/preprocessor.rb +273 -0
  68. data/lib/bitclust/rdcompiler.rb +507 -0
  69. data/lib/bitclust/refsdatabase.rb +66 -0
  70. data/lib/bitclust/requesthandler.rb +330 -0
  71. data/lib/bitclust/ridatabase.rb +349 -0
  72. data/lib/bitclust/rrdparser.rb +522 -0
  73. data/lib/bitclust/runner.rb +143 -0
  74. data/lib/bitclust/screen.rb +554 -0
  75. data/lib/bitclust/searcher.rb +518 -0
  76. data/lib/bitclust/server.rb +59 -0
  77. data/lib/bitclust/simplesearcher.rb +84 -0
  78. data/lib/bitclust/subcommand.rb +746 -0
  79. data/lib/bitclust/textutils.rb +51 -0
  80. data/lib/bitclust/version.rb +3 -0
  81. data/packer.rb +224 -0
  82. data/refe2.gemspec +29 -0
  83. data/server.exe +0 -0
  84. data/server.exy +159 -0
  85. data/server.rb +10 -0
  86. data/setup.rb +1596 -0
  87. data/standalone.rb +193 -0
  88. data/test/run_test.rb +15 -0
  89. data/test/test_bitclust.rb +81 -0
  90. data/test/test_entry.rb +39 -0
  91. data/test/test_functiondatabase.rb +55 -0
  92. data/test/test_libraryentry.rb +31 -0
  93. data/test/test_methoddatabase.rb +81 -0
  94. data/test/test_methodsignature.rb +14 -0
  95. data/test/test_nameutils.rb +324 -0
  96. data/test/test_preprocessor.rb +84 -0
  97. data/test/test_rdcompiler.rb +534 -0
  98. data/test/test_refsdatabase.rb +76 -0
  99. data/test/test_rrdparser.rb +26 -0
  100. data/test/test_runner.rb +102 -0
  101. data/test/test_simplesearcher.rb +48 -0
  102. data/theme/default/images/external.png +0 -0
  103. data/theme/default/rurema.png +0 -0
  104. data/theme/default/style.css +288 -0
  105. data/theme/default/test.css +254 -0
  106. data/theme/lillia/rurema.png +0 -0
  107. data/theme/lillia/style.css +331 -0
  108. data/theme/lillia/test.css +254 -0
  109. data/tools/bc-ancestors.rb +153 -0
  110. data/tools/bc-checkparams.rb +246 -0
  111. data/tools/bc-classes.rb +80 -0
  112. data/tools/bc-convert.rb +165 -0
  113. data/tools/bc-list.rb +63 -0
  114. data/tools/bc-methods.rb +171 -0
  115. data/tools/bc-preproc.rb +42 -0
  116. data/tools/bc-rdoc.rb +343 -0
  117. data/tools/bc-tochm.rb +301 -0
  118. data/tools/bc-tohtml.rb +125 -0
  119. data/tools/bc-tohtmlpackage.rb +241 -0
  120. data/tools/check-signature.rb +19 -0
  121. data/tools/forall-ruby.rb +20 -0
  122. data/tools/gencatalog.rb +69 -0
  123. data/tools/statrefm.rb +98 -0
  124. data/tools/stattodo.rb +150 -0
  125. data/tools/update-database.rb +146 -0
  126. data/view.cgi +6 -0
  127. metadata +222 -0
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # bc-tohtml.rb
4
+ #
5
+ # Copyright (c) 2006-2007 Minero Aoki
6
+ #
7
+ # This program is free software.
8
+ # You can distribute/modify this program under the Ruby License.
9
+ #
10
+
11
+ require 'pathname'
12
+
13
+ def srcdir_root
14
+ (Pathname.new(__FILE__).realpath.dirname + '..').cleanpath
15
+ end
16
+
17
+ $LOAD_PATH.unshift srcdir_root() + 'lib'
18
+
19
+ $KCODE = 'UTF-8' unless Object.const_defined?(:Encoding)
20
+
21
+ require 'bitclust'
22
+ require 'optparse'
23
+
24
+ def main
25
+ templatedir = srcdir_root() + 'data' + 'bitclust' + 'template.offline'
26
+ target = nil
27
+ baseurl = 'file://' + srcdir_root.to_s
28
+ parser = OptionParser.new
29
+ ver = '1.9.0'
30
+ parser.banner = "Usage: #{File.basename($0, '.*')} rdfile"
31
+ parser.on('--target=NAME', 'Compile NAME to HTML.') {|name|
32
+ target = name
33
+ }
34
+ parser.on('--force', '-f', 'Force to use rd_file template.') {|name|
35
+ @rd_file = true
36
+ }
37
+ parser.on('--ruby_version=VER', '--ruby=VER', 'Set Ruby version') {|v|
38
+ ver = v
39
+ }
40
+ parser.on('--db=DB', '--database=DB', 'Set database path') {|path|
41
+ db = BitClust::Database.new(path)
42
+ }
43
+ parser.on('--baseurl=URL', 'Base URL of generated HTML') {|url|
44
+ baseurl = url
45
+ }
46
+ parser.on('--templatedir=PATH', 'Template directory') {|path|
47
+ templatedir = path
48
+ }
49
+ parser.on('--help', 'Prints this message and quit.') {
50
+ puts parser.help
51
+ exit 0
52
+ }
53
+ parser.on('--capi', 'C API mode.') {
54
+ @capi = true
55
+ }
56
+ begin
57
+ parser.parse!
58
+ rescue OptionParser::ParseError => err
59
+ $stderr.puts err.message
60
+ $stderr.puts parser.help
61
+ exit 1
62
+ end
63
+ if ARGV.size > 1
64
+ $stderr.puts "too many arguments (expected 1)"
65
+ exit 1
66
+ end
67
+
68
+ db ||= BitClust::Database.dummy({'version' => ver})
69
+ manager = BitClust::ScreenManager.new(
70
+ :templatedir => templatedir,
71
+ :base_url => baseurl,
72
+ :cgi_url => baseurl,
73
+ :default_encoding => 'utf-8')
74
+ unless @rd_file
75
+ begin
76
+ if @capi
77
+ lib = BitClust::FunctionReferenceParser.parse_file(ARGV[0], {'version' => ver})
78
+ unless target
79
+ raise NotImplementedError, "generating a C API html without --target=NAME is not implemented yet."
80
+ end
81
+ else
82
+ lib = BitClust::RRDParser.parse_stdlib_file(ARGV[0], {'version' => ver})
83
+ end
84
+ entry = target ? lookup(lib, target) : lib
85
+ puts manager.entry_screen(entry, {:database => db}).body
86
+ return
87
+ rescue BitClust::ParseError => ex
88
+ $stderr.puts ex.message
89
+ $stderr.puts ex.backtrace[0], ex.backtrace[1..-1].map{|s| "\tfrom " + s}
90
+ end
91
+ end
92
+
93
+ ent = BitClust::DocEntry.new(db, ARGV[0])
94
+ ret = BitClust::Preprocessor.read(ARGV[0], {'version' => ver})
95
+ ent.source = ret
96
+ puts manager.doc_screen(ent, {:database => db} ).body
97
+ return
98
+ rescue BitClust::WriterError => err
99
+ $stderr.puts err.message
100
+ exit 1
101
+ end
102
+
103
+ def lookup(lib, key)
104
+ case
105
+ when @capi && BitClust::NameUtils.functionname?(key)
106
+ lib.find {|func| func.name == key}
107
+ when BitClust::NameUtils.method_spec?(key)
108
+ spec = BitClust::MethodSpec.parse(key)
109
+ if spec.constant?
110
+ begin
111
+ lib.fetch_class(key)
112
+ rescue BitClust::UserError
113
+ lib.fetch_methods(spec)
114
+ end
115
+ else
116
+ lib.fetch_methods(spec)
117
+ end
118
+ when BitClust::NameUtils.classname?(key)
119
+ lib.fetch_class(key)
120
+ else
121
+ raise BitClust::InvalidKey, "wrong search key: #{key.inspect}"
122
+ end
123
+ end
124
+
125
+ main
@@ -0,0 +1,241 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+ require 'pathname'
4
+
5
+ def srcdir_root
6
+ (Pathname.new(__FILE__).realpath.dirname + '..').cleanpath
7
+ end
8
+ $LOAD_PATH.unshift srcdir_root() + 'lib'
9
+
10
+ require 'bitclust'
11
+ require 'fileutils'
12
+ require 'optparse'
13
+
14
+ if Object.const_defined?(:Encoding)
15
+ Encoding.default_external = 'utf-8'
16
+ end
17
+
18
+ module BitClust
19
+
20
+ class URLMapperEx < URLMapper
21
+ def library_url(name)
22
+ if name == '/'
23
+ $bitclust_html_base + "/library/index.html"
24
+ else
25
+ $bitclust_html_base + "/library/#{encodename_package(name)}.html"
26
+ end
27
+ end
28
+
29
+ def class_url(name)
30
+ $bitclust_html_base + "/class/#{encodename_package(name)}.html"
31
+ end
32
+
33
+ def method_url(spec)
34
+ cname, tmark, mname = *split_method_spec(spec)
35
+ $bitclust_html_base +
36
+ "/method/#{encodename_package(cname)}/#{typemark2char(tmark)}/#{encodename_package(mname)}.html"
37
+ end
38
+
39
+ def function_url(name)
40
+ $bitclust_html_base + "/function/#{name.empty? ? 'index' : name}.html"
41
+ end
42
+
43
+ def document_url(name)
44
+ $bitclust_html_base + "/doc/#{encodename_package(name)}.html"
45
+ end
46
+
47
+ def css_url
48
+ $bitclust_html_base + "/" + @css_url
49
+ end
50
+
51
+ def favicon_url
52
+ $bitclust_html_base + "/" + @favicon_url
53
+ end
54
+
55
+ def library_index_url
56
+ $bitclust_html_base + "/library/index.html"
57
+ end
58
+
59
+ def function_index_url
60
+ $bitclust_html_base + "/function/index.html"
61
+ end
62
+
63
+ end
64
+ end
65
+
66
+ def main
67
+ prefix = Pathname.new('./db')
68
+ outputdir = Pathname.new('./doc')
69
+ templatedir = srcdir_root + 'data'+ 'bitclust' + 'template'
70
+ catalogdir = nil
71
+ verbose = true
72
+ parser = OptionParser.new
73
+ parser.on('-d', '--database=PATH', 'Database prefix') do |path|
74
+ prefix = Pathname.new(path).realpath
75
+ end
76
+ parser.on('-o', '--outputdir=PATH', 'Output directory') do |path|
77
+ begin
78
+ outputdir = Pathname.new(path).realpath
79
+ rescue Errno::ENOENT
80
+ FileUtils.mkdir_p(path, :verbose => verbose)
81
+ retry
82
+ end
83
+ end
84
+ parser.on('--catalog=PATH', 'Catalog directory') do |path|
85
+ catalogdir = Pathname.new(path).realpath
86
+ end
87
+ parser.on('--templatedir=PATH', 'Template directory') do |path|
88
+ templatedir = Pathname.new(path).realpath
89
+ end
90
+ parser.on('--fs-casesensitive', 'Filesystem is case-sensitive') do
91
+ $fs_casesensitive = true
92
+ end
93
+ parser.on('--[no-]quiet', 'Be quiet') do |quiet|
94
+ verbose = !quiet
95
+ end
96
+ parser.on('--help', 'Prints this message and quit') do
97
+ puts(parser.help)
98
+ exit(0)
99
+ end
100
+ begin
101
+ parser.parse!
102
+ rescue OptionParser::ParseError => err
103
+ STDERR.puts(err.message)
104
+ STDERR.puts(parser.help)
105
+ exit(1)
106
+ end
107
+
108
+ manager_config = {
109
+ :catalogdir => catalogdir,
110
+ :suffix => '.html',
111
+ :templatedir => templatedir,
112
+ :themedir => srcdir_root + 'theme' + 'default',
113
+ :css_url => 'style.css',
114
+ :favicon_url => 'rurema.png',
115
+ :cgi_url => '',
116
+ :tochm_mode => true
117
+ }
118
+ manager_config[:urlmapper] = BitClust::URLMapperEx.new(manager_config)
119
+
120
+ db = BitClust::MethodDatabase.new(prefix.to_s)
121
+ fdb = BitClust::FunctionDatabase.new(prefix.to_s)
122
+ manager = BitClust::ScreenManager.new(manager_config)
123
+ db.transaction do
124
+ methods = {}
125
+ db.methods.each_with_index do |entry, i|
126
+ method_name = entry.klass.name + entry.typemark + entry.name
127
+ (methods[method_name] ||= []) << entry
128
+ end
129
+ entries = db.docs + db.libraries.sort + db.classes.sort + methods.values
130
+ entries.each_with_index do |c, i|
131
+ create_html_file(c, manager, outputdir, db)
132
+ $stderr.puts("#{i}/#{entries.size} done") if i % 100 == 0 and verbose
133
+ end
134
+ end
135
+ fdb.transaction do
136
+ functions = {}
137
+ fdb.functions.each_with_index do |entry, i|
138
+ create_html_file(entry, manager, outputdir, fdb)
139
+ $stderr.puts("#{i} done") if i % 100 == 0 and verbose
140
+ end
141
+ end
142
+ $bitclust_html_base = '..'
143
+ create_file(outputdir + 'library/index.html',
144
+ manager.library_index_screen(db.libraries.sort, {:database => db}).body,
145
+ :verbose => verbose)
146
+ create_file(outputdir + 'class/index.html',
147
+ manager.class_index_screen(db.classes.sort, {:database => db}).body,
148
+ :verbose => verbose)
149
+ create_file(outputdir + 'function/index.html',
150
+ manager.function_index_screen(fdb.functions.sort, { :database => fdb }).body,
151
+ :verbose => verbose)
152
+ create_index_html(outputdir)
153
+ FileUtils.cp(manager_config[:themedir] + manager_config[:css_url],
154
+ outputdir.to_s, {:verbose => verbose, :preserve => true})
155
+ FileUtils.cp(manager_config[:themedir] + manager_config[:favicon_url],
156
+ outputdir.to_s, {:verbose => verbose, :preserve => true})
157
+ Dir.mktmpdir do |tmpdir|
158
+ FileUtils.cp_r(manager_config[:themedir] + 'images', tmpdir,
159
+ {:verbose => verbose, :preserve => true})
160
+ Dir.glob(File.join(tmpdir, 'images', '/**/.svn')).each do |d|
161
+ FileUtils.rm_r(d, {:verbose => verbose})
162
+ end
163
+ FileUtils.cp_r(File.join(tmpdir, 'images'), outputdir.to_s,
164
+ {:verbose => verbose, :preserve => true})
165
+ end
166
+ end
167
+
168
+ def encodename_package(str)
169
+ if $fs_casesensitive
170
+ BitClust::NameUtils.encodename_url(str)
171
+ else
172
+ BitClust::NameUtils.encodename_fs(str)
173
+ end
174
+ end
175
+
176
+ def create_index_html(outputdir)
177
+ path = outputdir + 'index.html'
178
+ File.open(path, 'w'){|io|
179
+ io.write <<HERE
180
+ <meta http-equiv="refresh" content="0; URL=doc/index.html">
181
+ <a href="doc/index.html">Go</a>
182
+ HERE
183
+ }
184
+ end
185
+
186
+ def create_html_file(entry, manager, outputdir, db)
187
+ e = entry.is_a?(Array) ? entry.sort.first : entry
188
+ case e.type_id
189
+ when :library, :class, :doc
190
+ $bitclust_html_base = '..'
191
+ path = outputdir + e.type_id.to_s + (encodename_package(e.name) + '.html')
192
+ create_html_file_p(entry, manager, path, db)
193
+ path.relative_path_from(outputdir).to_s
194
+ when :method
195
+ create_html_method_file(entry, manager, outputdir, db)
196
+ when :function
197
+ create_html_function_file(entry, manager, outputdir, db)
198
+ else
199
+ raise
200
+ end
201
+ e.unload
202
+ end
203
+
204
+ def create_html_method_file(entry, manager, outputdir, db)
205
+ path = nil
206
+ $bitclust_html_base = '../../..'
207
+ e = entry.is_a?(Array) ? entry.sort.first : entry
208
+ e.names.each{|name|
209
+ path = outputdir + e.type_id.to_s + encodename_package(e.klass.name) +
210
+ e.typechar + (encodename_package(name) + '.html')
211
+ create_html_file_p(entry, manager, path, db)
212
+ }
213
+ path.relative_path_from(outputdir).to_s
214
+ end
215
+
216
+ def create_html_function_file(entry, manager, outputdir, db)
217
+ path = nil
218
+ $bitclust_html_base = '..'
219
+ path = outputdir + entry.type_id.to_s + (entry.name + '.html')
220
+ create_html_file_p(entry, manager, path, db)
221
+ path.relative_path_from(outputdir).to_s
222
+ end
223
+
224
+ def create_html_file_p(entry, manager, path, db)
225
+ FileUtils.mkdir_p(path.dirname) unless path.dirname.directory?
226
+ html = manager.entry_screen(entry, {:database => db}).body
227
+ path.open('w') do |f|
228
+ f.write(html)
229
+ end
230
+ end
231
+
232
+ def create_file(path, str, options={})
233
+ verbose = options[:verbose]
234
+ STDERR.print("creating #{path} ...") if verbose
235
+ path.open('w') do |f|
236
+ f.write(str)
237
+ end
238
+ STDERR.puts(" done.") if verbose
239
+ end
240
+
241
+ main
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pathname'
4
+ $LOAD_PATH.unshift Pathname($0).realpath.dirname.dirname + 'lib'
5
+
6
+ require 'bitclust/methodsignature'
7
+
8
+ st = 0
9
+ ARGF.each do |line|
10
+ if /\A---/ =~ line
11
+ begin
12
+ BitClust::MethodSignature.parse(line)
13
+ rescue => err
14
+ $stderr.puts "#{ARGF.filename}:#{ARGF.file.lineno}: #{line.strip.inspect}"
15
+ st = 1
16
+ end
17
+ end
18
+ end
19
+ exit st
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pathname'
4
+
5
+ bindir = Pathname.new(__FILE__).realpath.dirname
6
+ $LOAD_PATH.unshift((bindir + '../lib').realpath)
7
+
8
+ require 'bitclust/crossrubyutils'
9
+
10
+ include BitClust::CrossRubyUtils
11
+
12
+ def main
13
+ ENV.delete('GEM_HOME')
14
+ forall_ruby(ENV['PATH']) do |ruby, ver|
15
+ puts ver
16
+ system ruby, *ARGV
17
+ end
18
+ end
19
+
20
+ main
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # gencatalog.rb
4
+ #
5
+ # Copyright (c) 2008 Minero Aoki
6
+ #
7
+ # This program is free software.
8
+ # You can distribute/modify this program under the Ruby License.
9
+ #
10
+
11
+ require 'optparse'
12
+
13
+ def main
14
+ catalog_path = nil
15
+ parser = OptionParser.new
16
+ parser.banner = "Usage: #{File.basename($0)} --merge=PATH [<file>...]"
17
+ parser.on('--merge=PATH', 'Current catalog file.') {|path|
18
+ catalog_path = path
19
+ }
20
+ parser.on('--help', 'Prints this message and quit.') {
21
+ puts parser.help
22
+ exit 0
23
+ }
24
+ begin
25
+ parser.parse!
26
+ rescue OptionParser::ParseError => err
27
+ $stderr.puts err
28
+ $stderr.puts parser.help
29
+ exit 1
30
+ end
31
+
32
+ h = collect_messages(ARGF)
33
+ h.update load_catalog(catalog_path) if catalog_path
34
+ print_catalog h
35
+ end
36
+
37
+ def print_catalog(h)
38
+ h.keys.sort.each do |key|
39
+ puts key
40
+ puts h[key]
41
+ end
42
+ end
43
+
44
+ def collect_messages(f)
45
+ h = {}
46
+ f.each do |line|
47
+ line.scan(/_\(
48
+ (?: "( (?:[^"]+|\\.)* )"
49
+ | '( (?:[^']+|\\.)* )'
50
+ )
51
+ /x) do
52
+ text = ($1 || $2).strip
53
+ h[text] = text unless text.empty?
54
+ end
55
+ end
56
+ h
57
+ end
58
+
59
+ def load_catalog(path)
60
+ h = {}
61
+ File.open(path) {|f|
62
+ f.each do |line|
63
+ h[line.chomp] = f.gets.chomp
64
+ end
65
+ }
66
+ h
67
+ end
68
+
69
+ main