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.
- data/AUTHORS.txt +9 -0
- data/History.txt +5539 -0
- data/Manifest.txt +115 -0
- data/PostInstall.txt +17 -0
- data/README.rdoc +149 -0
- data/Rakefile +28 -0
- data/bin/gonzui-db +167 -0
- data/bin/gonzui-import +177 -0
- data/bin/gonzui-remove +58 -0
- data/bin/gonzui-search +68 -0
- data/bin/gonzui-server +176 -0
- data/bin/gonzui-update +53 -0
- data/data/gonzui/catalog/catalog.ja +80 -0
- data/data/gonzui/doc/favicon.ico +0 -0
- data/data/gonzui/doc/folder.png +0 -0
- data/data/gonzui/doc/gonzui.css +279 -0
- data/data/gonzui/doc/gonzui.js +111 -0
- data/data/gonzui/doc/text.png +0 -0
- data/data/gonzuirc.sample +29 -0
- data/ext/autopack/autopack.c +88 -0
- data/ext/autopack/extconf.rb +3 -0
- data/ext/delta/delta.c +147 -0
- data/ext/delta/extconf.rb +5 -0
- data/ext/texttokenizer/extconf.rb +5 -0
- data/ext/texttokenizer/texttokenizer.c +93 -0
- data/ext/xmlformatter/extconf.rb +5 -0
- data/ext/xmlformatter/xmlformatter.c +207 -0
- data/lib/gonzui.rb +59 -0
- data/lib/gonzui/apt.rb +193 -0
- data/lib/gonzui/autopack.so +0 -0
- data/lib/gonzui/bdbdbm.rb +118 -0
- data/lib/gonzui/cmdapp.rb +14 -0
- data/lib/gonzui/cmdapp/app.rb +175 -0
- data/lib/gonzui/cmdapp/search.rb +134 -0
- data/lib/gonzui/config.rb +117 -0
- data/lib/gonzui/content.rb +19 -0
- data/lib/gonzui/dbm.rb +673 -0
- data/lib/gonzui/deindexer.rb +162 -0
- data/lib/gonzui/delta.rb +49 -0
- data/lib/gonzui/delta.so +0 -0
- data/lib/gonzui/extractor.rb +347 -0
- data/lib/gonzui/fetcher.rb +309 -0
- data/lib/gonzui/gettext.rb +144 -0
- data/lib/gonzui/importer.rb +84 -0
- data/lib/gonzui/indexer.rb +316 -0
- data/lib/gonzui/info.rb +80 -0
- data/lib/gonzui/license.rb +100 -0
- data/lib/gonzui/logger.rb +48 -0
- data/lib/gonzui/monitor.rb +177 -0
- data/lib/gonzui/progressbar.rb +235 -0
- data/lib/gonzui/remover.rb +38 -0
- data/lib/gonzui/searcher.rb +330 -0
- data/lib/gonzui/searchquery.rb +235 -0
- data/lib/gonzui/searchresult.rb +111 -0
- data/lib/gonzui/texttokenizer.so +0 -0
- data/lib/gonzui/updater.rb +254 -0
- data/lib/gonzui/util.rb +415 -0
- data/lib/gonzui/vcs.rb +128 -0
- data/lib/gonzui/webapp.rb +25 -0
- data/lib/gonzui/webapp/advsearch.rb +123 -0
- data/lib/gonzui/webapp/filehandler.rb +24 -0
- data/lib/gonzui/webapp/jsfeed.rb +61 -0
- data/lib/gonzui/webapp/markup.rb +445 -0
- data/lib/gonzui/webapp/search.rb +269 -0
- data/lib/gonzui/webapp/servlet.rb +319 -0
- data/lib/gonzui/webapp/snippet.rb +155 -0
- data/lib/gonzui/webapp/source.rb +37 -0
- data/lib/gonzui/webapp/stat.rb +137 -0
- data/lib/gonzui/webapp/top.rb +63 -0
- data/lib/gonzui/webapp/uri.rb +140 -0
- data/lib/gonzui/webapp/webrick.rb +48 -0
- data/lib/gonzui/webapp/xmlformatter.so +0 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/script/makemanifest.rb +21 -0
- data/tasks/extconf.rake +13 -0
- data/tasks/extconf/autopack.rake +43 -0
- data/tasks/extconf/delta.rake +43 -0
- data/tasks/extconf/texttokenizer.rake +43 -0
- data/tasks/extconf/xmlformatter.rake +43 -0
- data/test/_external_tools.rb +13 -0
- data/test/_test-util.rb +142 -0
- data/test/foo/Makefile.foo +66 -0
- data/test/foo/bar.c +5 -0
- data/test/foo/bar.h +6 -0
- data/test/foo/foo.c +25 -0
- data/test/foo/foo.spec +33 -0
- data/test/test_apt.rb +42 -0
- data/test/test_autopack_extn.rb +7 -0
- data/test/test_bdbdbm.rb +79 -0
- data/test/test_cmdapp-app.rb +35 -0
- data/test/test_cmdapp-search.rb +99 -0
- data/test/test_config.rb +28 -0
- data/test/test_content.rb +15 -0
- data/test/test_dbm.rb +171 -0
- data/test/test_deindexer.rb +50 -0
- data/test/test_delta.rb +66 -0
- data/test/test_extractor.rb +78 -0
- data/test/test_fetcher.rb +75 -0
- data/test/test_gettext.rb +50 -0
- data/test/test_gonzui.rb +11 -0
- data/test/test_helper.rb +10 -0
- data/test/test_importer.rb +56 -0
- data/test/test_indexer.rb +37 -0
- data/test/test_info.rb +82 -0
- data/test/test_license.rb +49 -0
- data/test/test_logger.rb +60 -0
- data/test/test_monitor.rb +23 -0
- data/test/test_searcher.rb +37 -0
- data/test/test_searchquery.rb +27 -0
- data/test/test_searchresult.rb +43 -0
- data/test/test_texttokenizer.rb +47 -0
- data/test/test_updater.rb +95 -0
- data/test/test_util.rb +149 -0
- data/test/test_vcs.rb +61 -0
- data/test/test_webapp-markup.rb +42 -0
- data/test/test_webapp-util.rb +19 -0
- data/test/test_webapp-xmlformatter.rb +19 -0
- 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
|
data/lib/gonzui/util.rb
ADDED
@@ -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
|
+
|