gonzui 1.2-x86-mswin32-60

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 (120) hide show
  1. data/AUTHORS.txt +9 -0
  2. data/History.txt +5539 -0
  3. data/Manifest.txt +115 -0
  4. data/PostInstall.txt +17 -0
  5. data/README.rdoc +149 -0
  6. data/Rakefile +28 -0
  7. data/bin/gonzui-db +167 -0
  8. data/bin/gonzui-import +177 -0
  9. data/bin/gonzui-remove +58 -0
  10. data/bin/gonzui-search +68 -0
  11. data/bin/gonzui-server +176 -0
  12. data/bin/gonzui-update +53 -0
  13. data/data/gonzui/catalog/catalog.ja +80 -0
  14. data/data/gonzui/doc/favicon.ico +0 -0
  15. data/data/gonzui/doc/folder.png +0 -0
  16. data/data/gonzui/doc/gonzui.css +279 -0
  17. data/data/gonzui/doc/gonzui.js +111 -0
  18. data/data/gonzui/doc/text.png +0 -0
  19. data/data/gonzuirc.sample +29 -0
  20. data/ext/autopack/autopack.c +88 -0
  21. data/ext/autopack/extconf.rb +3 -0
  22. data/ext/delta/delta.c +147 -0
  23. data/ext/delta/extconf.rb +5 -0
  24. data/ext/texttokenizer/extconf.rb +5 -0
  25. data/ext/texttokenizer/texttokenizer.c +93 -0
  26. data/ext/xmlformatter/extconf.rb +5 -0
  27. data/ext/xmlformatter/xmlformatter.c +207 -0
  28. data/lib/gonzui.rb +59 -0
  29. data/lib/gonzui/apt.rb +193 -0
  30. data/lib/gonzui/autopack.so +0 -0
  31. data/lib/gonzui/bdbdbm.rb +118 -0
  32. data/lib/gonzui/cmdapp.rb +14 -0
  33. data/lib/gonzui/cmdapp/app.rb +175 -0
  34. data/lib/gonzui/cmdapp/search.rb +134 -0
  35. data/lib/gonzui/config.rb +117 -0
  36. data/lib/gonzui/content.rb +19 -0
  37. data/lib/gonzui/dbm.rb +673 -0
  38. data/lib/gonzui/deindexer.rb +162 -0
  39. data/lib/gonzui/delta.rb +49 -0
  40. data/lib/gonzui/delta.so +0 -0
  41. data/lib/gonzui/extractor.rb +347 -0
  42. data/lib/gonzui/fetcher.rb +309 -0
  43. data/lib/gonzui/gettext.rb +144 -0
  44. data/lib/gonzui/importer.rb +84 -0
  45. data/lib/gonzui/indexer.rb +316 -0
  46. data/lib/gonzui/info.rb +80 -0
  47. data/lib/gonzui/license.rb +100 -0
  48. data/lib/gonzui/logger.rb +48 -0
  49. data/lib/gonzui/monitor.rb +177 -0
  50. data/lib/gonzui/progressbar.rb +235 -0
  51. data/lib/gonzui/remover.rb +38 -0
  52. data/lib/gonzui/searcher.rb +330 -0
  53. data/lib/gonzui/searchquery.rb +235 -0
  54. data/lib/gonzui/searchresult.rb +111 -0
  55. data/lib/gonzui/texttokenizer.so +0 -0
  56. data/lib/gonzui/updater.rb +254 -0
  57. data/lib/gonzui/util.rb +415 -0
  58. data/lib/gonzui/vcs.rb +128 -0
  59. data/lib/gonzui/webapp.rb +25 -0
  60. data/lib/gonzui/webapp/advsearch.rb +123 -0
  61. data/lib/gonzui/webapp/filehandler.rb +24 -0
  62. data/lib/gonzui/webapp/jsfeed.rb +61 -0
  63. data/lib/gonzui/webapp/markup.rb +445 -0
  64. data/lib/gonzui/webapp/search.rb +269 -0
  65. data/lib/gonzui/webapp/servlet.rb +319 -0
  66. data/lib/gonzui/webapp/snippet.rb +155 -0
  67. data/lib/gonzui/webapp/source.rb +37 -0
  68. data/lib/gonzui/webapp/stat.rb +137 -0
  69. data/lib/gonzui/webapp/top.rb +63 -0
  70. data/lib/gonzui/webapp/uri.rb +140 -0
  71. data/lib/gonzui/webapp/webrick.rb +48 -0
  72. data/lib/gonzui/webapp/xmlformatter.so +0 -0
  73. data/script/console +10 -0
  74. data/script/destroy +14 -0
  75. data/script/generate +14 -0
  76. data/script/makemanifest.rb +21 -0
  77. data/tasks/extconf.rake +13 -0
  78. data/tasks/extconf/autopack.rake +43 -0
  79. data/tasks/extconf/delta.rake +43 -0
  80. data/tasks/extconf/texttokenizer.rake +43 -0
  81. data/tasks/extconf/xmlformatter.rake +43 -0
  82. data/test/_external_tools.rb +13 -0
  83. data/test/_test-util.rb +142 -0
  84. data/test/foo/Makefile.foo +66 -0
  85. data/test/foo/bar.c +5 -0
  86. data/test/foo/bar.h +6 -0
  87. data/test/foo/foo.c +25 -0
  88. data/test/foo/foo.spec +33 -0
  89. data/test/test_apt.rb +42 -0
  90. data/test/test_autopack_extn.rb +7 -0
  91. data/test/test_bdbdbm.rb +79 -0
  92. data/test/test_cmdapp-app.rb +35 -0
  93. data/test/test_cmdapp-search.rb +99 -0
  94. data/test/test_config.rb +28 -0
  95. data/test/test_content.rb +15 -0
  96. data/test/test_dbm.rb +171 -0
  97. data/test/test_deindexer.rb +50 -0
  98. data/test/test_delta.rb +66 -0
  99. data/test/test_extractor.rb +78 -0
  100. data/test/test_fetcher.rb +75 -0
  101. data/test/test_gettext.rb +50 -0
  102. data/test/test_gonzui.rb +11 -0
  103. data/test/test_helper.rb +10 -0
  104. data/test/test_importer.rb +56 -0
  105. data/test/test_indexer.rb +37 -0
  106. data/test/test_info.rb +82 -0
  107. data/test/test_license.rb +49 -0
  108. data/test/test_logger.rb +60 -0
  109. data/test/test_monitor.rb +23 -0
  110. data/test/test_searcher.rb +37 -0
  111. data/test/test_searchquery.rb +27 -0
  112. data/test/test_searchresult.rb +43 -0
  113. data/test/test_texttokenizer.rb +47 -0
  114. data/test/test_updater.rb +95 -0
  115. data/test/test_util.rb +149 -0
  116. data/test/test_vcs.rb +61 -0
  117. data/test/test_webapp-markup.rb +42 -0
  118. data/test/test_webapp-util.rb +19 -0
  119. data/test/test_webapp-xmlformatter.rb +19 -0
  120. metadata +292 -0
@@ -0,0 +1,111 @@
1
+ #
2
+ # searchresult.rb - search result implementation
3
+ #
4
+ # Copyright (C) 2004-2005 Satoru Takabayashi <satoru@namazu.org>
5
+ # All rights reserved.
6
+ # This is free software with ABSOLUTELY NO WARRANTY.
7
+ #
8
+ # You can redistribute it and/or modify it under the terms of
9
+ # the GNU General Public License version 2.
10
+ #
11
+
12
+ module Gonzui
13
+ class SearchResult
14
+ include Enumerable
15
+
16
+ def initialize
17
+ @items = []
18
+ @limit_exceeded = false
19
+ end
20
+ attr_accessor :limit_exceeded
21
+
22
+ public
23
+ def [] (i)
24
+ @items[i]
25
+ end
26
+
27
+ def clear
28
+ @items.clear
29
+ end
30
+
31
+ def each
32
+ @items.each {|item| yield(item) }
33
+ end
34
+
35
+ def each_from(from)
36
+ (from...@items.length).each {|i|
37
+ yield(@items[i])
38
+ }
39
+ end
40
+
41
+ def empty?
42
+ @items.empty?
43
+ end
44
+
45
+ def first
46
+ @items.first
47
+ end
48
+
49
+ def last
50
+ @items.last
51
+ end
52
+
53
+ def length
54
+ @items.length
55
+ end
56
+ alias :nhits :length
57
+
58
+ def limit_exceeded?
59
+ @limit_exceeded
60
+ end
61
+
62
+ def push(item)
63
+ @items.push(item)
64
+ end
65
+
66
+ def single?
67
+ @items.length == 1 and not @items.first.has_more?
68
+ end
69
+
70
+ def single_path?
71
+ @items.length == 1 and @items.first.has_more_in_path?
72
+ end
73
+ end
74
+
75
+ class ResultItem
76
+ def initialize(package_id, path_id)
77
+ @package_id = package_id
78
+ @path_id = path_id
79
+ @list = []
80
+ @grouped_by = nil
81
+ end
82
+ attr_reader :package_id
83
+ attr_reader :path_id
84
+ attr_reader :list
85
+
86
+ public
87
+ def push(occ)
88
+ @list.push(occ)
89
+ end
90
+
91
+ def has_more?
92
+ not @grouped_by.nil?
93
+ end
94
+
95
+ def has_more_in_package?
96
+ @grouped_by == :package
97
+ end
98
+
99
+ def has_more_in_path?
100
+ @grouped_by == :path
101
+ end
102
+
103
+ def has_more_in_package
104
+ @grouped_by = :package
105
+ end
106
+
107
+ def has_more_in_path
108
+ @grouped_by = :path
109
+ end
110
+ end
111
+ end
Binary file
@@ -0,0 +1,254 @@
1
+ #
2
+ # updater.rb - update contents in gonzui.db
3
+ #
4
+ # Copyright (C) 2004-2005 Satoru Takabayashi <satoru@namazu.org>
5
+ # All rights reserved.
6
+ # This is free software with ABSOLUTELY NO WARRANTY.
7
+ #
8
+ # You can redistribute it and/or modify it under the terms of
9
+ # the GNU General Public License version 2.
10
+ #
11
+
12
+ require 'digest/md5'
13
+ require "gonzui/progressbar"
14
+
15
+ module Gonzui
16
+ class AbstractUpdater
17
+ include Util
18
+
19
+ def initialize(config, options = {})
20
+ @start_time = Time.now
21
+ @dbm = DBM.open(config)
22
+ @config = config
23
+ @ncontents = 0
24
+ @npackages = 0
25
+ @show_progress = options[:show_progress]
26
+ end
27
+
28
+ private
29
+ def make_progress_bar(title, total)
30
+ klass = if @config.verbose
31
+ NullObject
32
+ elsif @show_progress
33
+ ProgressBar
34
+ else
35
+ NullObject
36
+ end
37
+ return klass.new(title, total)
38
+ end
39
+
40
+ def do_task_name
41
+ NotImplementedError.new
42
+ end
43
+
44
+ def task_name
45
+ do_task_name
46
+ end
47
+
48
+ def deindex_content_internal(normalized_path)
49
+ deindexer = Deindexer.new(@config, @dbm, normalized_path)
50
+ deindexer.deindex
51
+ end
52
+
53
+ def deindex_content(normalized_path)
54
+ protect_from_signals {
55
+ deindex_content_internal(normalized_path)
56
+ @ncontents += 1
57
+ }
58
+ end
59
+
60
+ def index_content_internal(source_uri, normalized_path, content,
61
+ options = {})
62
+ indexer = Indexer.new(@config, @dbm, source_uri, normalized_path,
63
+ content, options)
64
+ indexer.index
65
+ end
66
+
67
+ def index_content(source_uri, normalized_path, content, options = {})
68
+ protect_from_signals {
69
+ index_content_internal(source_uri, normalized_path, content, options)
70
+ @ncontents += 1
71
+ }
72
+ end
73
+
74
+ def update_content(source_uri, normalized_path, content, options = {})
75
+ protect_from_signals {
76
+ deindex_content_internal(normalized_path)
77
+ index_content_internal(source_uri, normalized_path, content, options)
78
+ @ncontents += 1
79
+ }
80
+ end
81
+
82
+ public
83
+ def summary
84
+ elapsed = Time.now - @start_time
85
+ format = "%d contents of %d packages %s in %.2f sec. (%.2f contents / sec.)\n"
86
+ summary = sprintf(format, @ncontents, @npackages, task_name, elapsed,
87
+ @ncontents / elapsed)
88
+ return summary
89
+ end
90
+
91
+ def finish
92
+ @dbm.close
93
+ end
94
+ end
95
+
96
+ class UpdateDiff
97
+ def initialize(config, dbm, fetcher, package_id, package_name,
98
+ options = {})
99
+ @config = config
100
+ @dbm = dbm
101
+ @fetcher = fetcher
102
+ @package_id = package_id
103
+ @package_name = package_name
104
+ @exclude_pattern = options[:exclude_pattern]
105
+
106
+ @paths_to_be_removed = []
107
+ @paths_to_be_added = []
108
+ @paths_to_be_updated = []
109
+
110
+ @paths_in_db = {}
111
+ @dbm.get_path_ids(package_id).each {|path_id|
112
+ normalized_path = @dbm.get_path(path_id)
113
+ @paths_in_db[normalized_path] = :unseen
114
+ }
115
+ collect
116
+ end
117
+
118
+ private
119
+ def file_updated?(relative_path, normalized_path)
120
+ path_id = @dbm.get_path_id(normalized_path)
121
+ content = nil
122
+ begin
123
+ content = @fetcher.fetch(relative_path)
124
+ rescue => e
125
+ return false # deleted after collecting a list
126
+ end
127
+ content_hash = Digest::MD5.hexdigest(content.text)
128
+ return content_hash != @dbm.get_content_hash(path_id)
129
+ end
130
+
131
+ def push_added_path(relative_path, normalized_path)
132
+ @paths_to_be_added.push([relative_path, normalized_path])
133
+ end
134
+
135
+ def push_updated_path(relative_path, normalized_path)
136
+ @paths_to_be_updated.push([relative_path, normalized_path])
137
+ end
138
+
139
+ def push_removed_path(normalized_path)
140
+ @paths_to_be_removed.push(normalized_path)
141
+ end
142
+
143
+ def collect
144
+ relative_paths = @fetcher.collect
145
+ relative_paths.each {|relative_path|
146
+ normalized_path = File.join(@package_name, relative_path)
147
+ @paths_in_db[normalized_path] = :seen
148
+ if @dbm.has_path?(normalized_path)
149
+ if file_updated?(relative_path, normalized_path)
150
+ push_updated_path(relative_path, normalized_path)
151
+ end
152
+ else
153
+ push_added_path(relative_path, normalized_path)
154
+ end
155
+ }
156
+ @paths_in_db.each {|normalized_path, mark|
157
+ push_removed_path(normalized_path) if mark == :unseen
158
+ }
159
+ end
160
+
161
+ public
162
+ def each_added_path
163
+ @paths_to_be_added.each {|relative_path, normalized_path|
164
+ yield(relative_path, normalized_path)
165
+ }
166
+ end
167
+
168
+ def each_updated_path
169
+ @paths_to_be_updated.each {|relative_path, normalized_path|
170
+ yield(relative_path, normalized_path)
171
+ }
172
+ end
173
+
174
+ def each_removed_path
175
+ @paths_to_be_removed.each {|normalized_path|
176
+ yield(normalized_path)
177
+ }
178
+ end
179
+
180
+ def ncontents
181
+ @paths_to_be_added.length + @paths_to_be_updated.length +
182
+ @paths_to_be_removed.length
183
+ end
184
+ end
185
+
186
+ class SourceDisappeared < GonzuiError; end
187
+
188
+ class Updater < AbstractUpdater
189
+ private
190
+ def do_task_name
191
+ "updated"
192
+ end
193
+
194
+ def make_fetcher(config, source_uri, options)
195
+ return Fetcher.new(config, source_uri, options)
196
+ rescue FetchFailed
197
+ raise SourceDisappeared.new
198
+ end
199
+
200
+ def update_package_internal(diff, fetcher, package_name,
201
+ source_uri, options)
202
+ pbar = make_progress_bar(package_name, diff.ncontents)
203
+ diff.each_removed_path {|normalized_path|
204
+ deindex_content(normalized_path)
205
+ pbar.inc
206
+ }
207
+ diff.each_added_path {|relative_path, normalized_path|
208
+ content = fetcher.fetch(relative_path)
209
+ index_content(source_uri, normalized_path, content, options)
210
+ pbar.inc
211
+ }
212
+ diff.each_updated_path {|relative_path, normalized_path|
213
+ content = fetcher.fetch(relative_path)
214
+ update_content(source_uri, normalized_path, content, options)
215
+ pbar.inc
216
+ }
217
+ pbar.finish
218
+ end
219
+
220
+ def update_package(package_name)
221
+ package_id = @dbm.get_package_id(package_name)
222
+ source_uri = URI.parse(@dbm.get_source_uri(package_id))
223
+ options = @dbm.get_package_options(package_id)
224
+ fetcher = make_fetcher(@config, source_uri, options)
225
+ diff = UpdateDiff.new(@config, @dbm, fetcher, package_id,
226
+ package_name, options)
227
+ if diff.ncontents > 0
228
+ update_package_internal(diff, fetcher, package_name,
229
+ source_uri, options)
230
+ @npackages += 1
231
+ return true
232
+ else
233
+ return false
234
+ end
235
+ end
236
+
237
+ public
238
+ def update
239
+ # Don't use #each_package_name to avoid
240
+ # deadlock. #each_package_name creates and holds a
241
+ # cursor while an iteration
242
+ @dbm.get_package_names.each {|package_name|
243
+ begin
244
+ updated_p = update_package(package_name)
245
+ yield(package_name) if block_given? and updated_p
246
+ rescue SourceDisappeared
247
+ wprintf("%s: source disappeared", package_name)
248
+ ensure
249
+ @dbm.flush_cache
250
+ end
251
+ }
252
+ end
253
+ end
254
+ end
@@ -0,0 +1,415 @@
1
+ #
2
+ # util.rb - utility functions
3
+ #
4
+ # Copyright (C) 2004-2005 Satoru Takabayashi <satoru@namazu.org>
5
+ # All rights reserved.
6
+ # This is free software with ABSOLUTELY NO WARRANTY.
7
+ #
8
+ # You can redistribute it and/or modify it under the terms of
9
+ # the GNU General Public License version 2.
10
+ #
11
+
12
+ require 'find'
13
+ require 'fileutils'
14
+ require 'iconv'
15
+ require 'benchmark'
16
+ require 'uri'
17
+ require 'thread'
18
+
19
+ module URI
20
+ module_function
21
+ def path_join(*fragments)
22
+ if fragments.empty?
23
+ return ""
24
+ elsif fragments.length == 1
25
+ return fragments.fileutils
26
+ else
27
+ return fragments.map {|fragment| fragment.chomp("/") }.join("/")
28
+ end
29
+ end
30
+
31
+ def from_path(path)
32
+ path = File.expand_path(path)
33
+ if (/^([a-zA-Z]:|\\\\)(.*)$/ =~ path)
34
+ u = URI::Generic.new( "file", nil, $1, nil, nil, $2, nil, nil, nil, false )
35
+ def u.to_s
36
+ self.host + self.path
37
+ end
38
+ return u
39
+ else
40
+ path = path.gsub(/#{Regexp.quote(File::SEPARATOR)}/, "/")
41
+ return URI.parse(sprintf("file://%s", path))
42
+ end
43
+ end
44
+
45
+ def for_apt(package_name)
46
+ URI.parse(sprintf("apt-get://apt-get/%s", package_name))
47
+ end
48
+
49
+ def for_cvs(repository, mozule)
50
+ if m = /^(?:(.+?)@)?(.+)$/.match(repository)
51
+ prefix = m[1]
52
+ root = m[2]
53
+ str = sprintf("cvs://%s?module=%s", root, mozule)
54
+ str << sprintf("&prefix=%s", prefix) if prefix
55
+ return URI.parse(str)
56
+ else
57
+ raise "malformed repository: #{repository}"
58
+ end
59
+ end
60
+
61
+ def for_svn(repository, mozule)
62
+ uri = URI.parse(repository)
63
+ query = sprintf("module=%s", mozule)
64
+ uri = URI.from_path(repository) unless uri.absolute?
65
+ # replace schemes other than "svn" such as "file" and "http"
66
+ # with "svn" and preserve the original scheme in a query.
67
+ unless uri.scheme == "svn"
68
+ query << sprintf("&original_scheme=%s", uri.scheme)
69
+ uri.scheme = "svn"
70
+ end
71
+ uri.query = query
72
+ return uri
73
+ end
74
+
75
+ def for_git(repository)
76
+ uri = URI.parse(repository)
77
+ uri = URI.from_path(repository) unless uri.absolute?
78
+ return uri
79
+ end
80
+ end
81
+
82
+ class NullObject
83
+ def initialize(*args)
84
+ end
85
+ def method_missing(name, *args)
86
+ end
87
+ end
88
+
89
+ module FileUtils
90
+ module_function
91
+ def chmod_r(mode, *paths)
92
+ Find.find(*paths) {|path|
93
+ File.chmod(mode, path) if !File.symlink?(path)
94
+ }
95
+ end
96
+
97
+ def fix_permission(directory)
98
+ Find.find(directory) {|file_name|
99
+ stat = File.lstat(file_name)
100
+ if stat.directory?
101
+ if stat.mode & 0777 != (stat.mode | 0700) & 0755
102
+ File.chmod((stat.mode | 0700) & 0755, file_name)
103
+ end
104
+ elsif stat.file?
105
+ if stat.mode & 0666 != (stat.mode | 0600) & 0644
106
+ File.chmod((stat.mode | 0600) & 0644, file_name)
107
+ end
108
+ end
109
+ }
110
+ end
111
+
112
+ def rm_rff(path)
113
+ fix_permission(path)
114
+ rm_rf(path)
115
+ end
116
+ end
117
+
118
+ class String
119
+ def clear
120
+ replace("")
121
+ end
122
+
123
+ def prechop
124
+ if m = /^./m.match(self)
125
+ return m.post_match
126
+ else
127
+ return ""
128
+ end
129
+ end
130
+
131
+ def substring(range)
132
+ raise unless range.exclude_end?
133
+ self[range.first, range.last - range.first]
134
+ end
135
+
136
+ def line_range(byteno)
137
+ head = if self[byteno] == ?\n
138
+ byteno
139
+ else
140
+ (self.rindex(?\n, byteno) or -1) + 1
141
+ end
142
+ tail = (self.index(?\n, byteno) or self.length)
143
+ return (head...tail)
144
+ end
145
+
146
+ def each_line_range(byteno, ncontexts)
147
+ raise unless block_given?
148
+ lines = []
149
+ center = self.line_range(byteno)
150
+ lines.push([0, center])
151
+
152
+ head = center.first
153
+ ncontexts.times {|i|
154
+ pos = head - 2
155
+ pos += 1 if self[pos] == ?\n # empty line
156
+ break if pos <= 0
157
+ range = self.line_range(pos)
158
+ head = range.first
159
+ lines.unshift([0 - i - 1, range])
160
+ }
161
+
162
+ tail = center.last
163
+ ncontexts.times {|i|
164
+ pos = tail + 1
165
+ break if pos >= self.length
166
+ range = self.line_range(pos)
167
+ tail = range.last
168
+ lines.push([i + 1, range])
169
+ }
170
+
171
+ lines.each {|lineno_offset, range|
172
+ yield(lineno_offset, range)
173
+ }
174
+ end
175
+
176
+ def untabify
177
+ new = ""
178
+ self.each_line {|line|
179
+ true while line.gsub!(/\t+/) {
180
+ ' ' * ($&.length * 8 - $`.length % 8) #`)
181
+ }
182
+ new << line
183
+ }
184
+ return new
185
+ end
186
+ end
187
+
188
+ class Dir
189
+ def self.entries_without_dots(directory)
190
+ entries(directory).find_all {|e| e != "." and e != ".." }
191
+ end
192
+
193
+ def self.all_files(directory)
194
+ file_names = []
195
+ Find.find(directory) {|file_name|
196
+ next unless File.file?(file_name)
197
+ file_names.push(file_name)
198
+ }
199
+ return file_names.sort!
200
+ end
201
+ end
202
+
203
+ class File
204
+ # FIXME: Use pathname.rb ?
205
+ def self.relative_path(path, base)
206
+ return "" if path == base
207
+ re_terminated_with_path_separator = /#{File::SEPARATOR}$/
208
+ sep = if re_terminated_with_path_separator.match(base)
209
+ ""
210
+ else
211
+ File::SEPARATOR
212
+ end
213
+ pattern = sprintf("^%s%s", Regexp.quote(base), sep)
214
+ path.sub(Regexp.new(pattern), "")
215
+ end
216
+
217
+ def self.any_exist?(path)
218
+ File.exist?(path) or File.symlink?(path)
219
+ end
220
+ end
221
+
222
+ class Array
223
+ def devide(n)
224
+ if self.empty?
225
+ []
226
+ elsif self.length < n
227
+ [self]
228
+ else
229
+ (0...(self.length / n)).map {|x| self[x * n, n] }
230
+ end
231
+ end
232
+ end
233
+
234
+ module Gonzui
235
+ module Util
236
+ module_function
237
+ def program_name
238
+ File.basename($0)
239
+ end
240
+
241
+ def windows?
242
+ /-(mswin32|cygwin|mingw|bccwin)/.match(RUBY_PLATFORM)
243
+ end
244
+
245
+ def unix?
246
+ not windows?
247
+ end
248
+
249
+ def command_exist?(command)
250
+ paths = (ENV['PATH'] or "").split(File::PATH_SEPARATOR)
251
+ paths.each {|path|
252
+ return true if File.executable?(File.join(path, command))
253
+ # windows
254
+ return true if File.executable?(File.join(path, command + ".exe"))
255
+ }
256
+ return false
257
+ end
258
+
259
+ class CommandNotFoundError < GonzuiError; end
260
+ def require_command(command)
261
+ raise CommandNotFoundError.new("#{command}: command not found") unless
262
+ command_exist?(command)
263
+ return true
264
+ end
265
+
266
+ def shell_escape(file_name)
267
+ if (/mswin|mingw|bccwin/ =~ RUBY_PLATFORM)
268
+ fn = file_name
269
+ fn.gsub!(/["]/, "")
270
+ if /^\w+:\/\// =~ fn
271
+ # file://c/temp => file:///c:/temp
272
+ fn.sub!(/^file:\/\/\/?(\w)\//, "file:///\\1:/")
273
+ else
274
+ fn.gsub!(/[\/]/, "\\")
275
+ end
276
+ '"' + fn + '"'
277
+ else
278
+ '"' + file_name.gsub(/([$"\\`])/, "\\\\\\1") + '"'
279
+ end
280
+ end
281
+
282
+ def wprintf(format, *args)
283
+ STDERR.printf(program_name + ": " + format + "\n", *args)
284
+ end
285
+
286
+ def eprintf(format, *args)
287
+ wprintf(format, *args)
288
+ exit(1)
289
+ end
290
+
291
+ @@verbosity = false
292
+ def set_verbosity(verbosity)
293
+ @@verbosity = verbosity
294
+ end
295
+
296
+ def vprintf(format, *args)
297
+ printf(format + "\n", *args) if @@verbosity
298
+ end
299
+
300
+ def commify(number)
301
+ numstr = number.to_s
302
+ true while numstr.sub!(/^([-+]?\d+)(\d{3})/, '\1,\2')
303
+ return numstr
304
+ end
305
+
306
+ def format_bytes(bytes)
307
+ if bytes < 1024
308
+ sprintf("%dB", bytes)
309
+ elsif bytes < 1024 * 1000 # 1000kb
310
+ sprintf("%.1fKB", bytes.to_f / 1024)
311
+ elsif bytes < 1024 * 1024 * 1000 # 1000mb
312
+ sprintf("%.1fMB", bytes.to_f / 1024 / 1024)
313
+ else
314
+ sprintf("%.1fGB", bytes.to_f / 1024 / 1024 / 1024)
315
+ end
316
+ end
317
+
318
+ def benchmark
319
+ result = nil
320
+ Benchmark.bm {|x|
321
+ x.report {
322
+ result = yield
323
+ }
324
+ }
325
+ return result
326
+ end
327
+
328
+ # Use a global mutex to make the method thread-safe
329
+ $protect_from_signals_mutex = Mutex.new
330
+ def protect_from_signals
331
+ $protect_from_signals_mutex.synchronize {
332
+ interrupted = false
333
+ previous_handlers = {}
334
+ signals = ["INT", "TERM"]
335
+
336
+ signals.each {|signal|
337
+ previous_handlers[signal] = trap(signal) { interrupted = true }
338
+ }
339
+ yield
340
+ previous_handlers.each {|signal, handler|
341
+ trap(signal, handler)
342
+ }
343
+ raise Interrupt if interrupted
344
+ }
345
+ end
346
+
347
+ class AssertionFailed < StandardError; end
348
+
349
+ def assert_non_nil(object)
350
+ raise AssertionFailed.new if object.nil?
351
+ end
352
+
353
+ def assert_not_reached
354
+ raise AssertionFailed.new
355
+ end
356
+
357
+ def assert_equal(expected, value)
358
+ raise AssertionFailed.new unless expected == value
359
+ end
360
+
361
+ def assert_equal_all(*values)
362
+ first = values.first
363
+ unless values.all? {|value| first == value }
364
+ raise AssertionFailed.new
365
+ end
366
+ end
367
+
368
+ def assert(bool)
369
+ raise AssertionFailed.new unless bool
370
+ end
371
+ end
372
+
373
+ module UTF8
374
+ Preference = ["iso-2022-jp", "euc-jp", "utf-8", "shift_jis",
375
+ "cp932", "iso-8859-1", "ascii"]
376
+
377
+ module_function
378
+ def set_preference(preference)
379
+ Preference.replace(preference)
380
+ end
381
+
382
+ def to_utf8(str)
383
+ return str, "us-ascii" if /\A[\r\n\t\x20-\x7e]*\Z/n.match(str)
384
+ Preference.each {|name|
385
+ begin
386
+ return Iconv.conv("UTF-8", name, str), name
387
+ rescue Iconv::IllegalSequence, ArgumentError
388
+ end
389
+ }
390
+ return str, "binary"
391
+ end
392
+ end
393
+
394
+ module TemporaryDirectoryUtil
395
+ attr_reader :temporary_directory
396
+
397
+ def prepare_temporary_directory
398
+ raise "temporary directory not set" if temporary_directory.nil?
399
+ raise "#{temporary_directory}: exists" if
400
+ File.any_exist?(temporary_directory)
401
+ File.mkpath(temporary_directory)
402
+ end
403
+
404
+ def clean_temporary_directory
405
+ raise "temporary directory not set" if temporary_directory.nil?
406
+ FileUtils.rm_rff(temporary_directory)
407
+ end
408
+
409
+ def set_temporary_directory(directory)
410
+ base_name = ["gonzui", "tmp", Process.pid, self.object_id].join(".")
411
+ @temporary_directory = File.join(directory, base_name)
412
+ end
413
+ end
414
+ end
415
+