fiddle 1.0.0.beta1 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/LICENSE.txt +20 -19
- data/README.md +74 -6
- data/Rakefile +4 -6
- data/bin/downloader.rb +331 -0
- data/bin/extlibs.rb +262 -0
- data/ext/fiddle/closure.c +6 -5
- data/ext/fiddle/conversions.c +161 -16
- data/ext/fiddle/conversions.h +14 -4
- data/ext/fiddle/depend +83 -0
- data/ext/fiddle/extconf.rb +68 -31
- data/ext/fiddle/extlibs +13 -2
- data/ext/fiddle/fiddle.c +46 -35
- data/ext/fiddle/fiddle.h +35 -1
- data/ext/fiddle/function.c +220 -81
- data/ext/fiddle/handle.c +10 -12
- data/ext/fiddle/pinned.c +123 -0
- data/ext/fiddle/pointer.c +106 -24
- data/ext/fiddle/win32/fficonfig.h +0 -0
- data/ext/fiddle/win32/libffi-config.rb +2 -2
- data/ext/fiddle/win32/libffi.mk.tmpl +0 -0
- data/fiddle.gemspec +50 -8
- data/lib/fiddle.rb +4 -2
- data/lib/fiddle/closure.rb +1 -1
- data/lib/fiddle/cparser.rb +33 -3
- data/lib/fiddle/function.rb +1 -1
- data/lib/fiddle/import.rb +12 -10
- data/lib/fiddle/pack.rb +16 -9
- data/lib/fiddle/struct.rb +268 -44
- data/lib/fiddle/types.rb +1 -1
- data/lib/fiddle/value.rb +19 -10
- data/lib/fiddle/version.rb +3 -0
- metadata +18 -30
- data/.gitignore +0 -12
- data/.travis.yml +0 -5
- data/Gemfile +0 -4
- data/bin/console +0 -14
- data/bin/setup +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: bb11df53ed0c5fb29347565ba3c56fb21ec02fe764db7f63184c1ebe5dd9ea76
|
4
|
+
data.tar.gz: 3f1571515de5917bfb3215fa45382ca59808ca94d5a0c4c966c1e924f6ed9f30
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e6f266dc69d93931746b269af9698a582f1ad59430e3a35d25073e5193d12d19a94021aac5acfdee8c8c72cda6359c2a6b528e2df7d8b695c8a23ec827e1a8c4
|
7
|
+
data.tar.gz: 884144e33963f0d549c90dbee74893663512f1e4eb216082cb332bb4fd62303969cbc77a03804b3c7f5f5b8a2916a96dc284c0857174ef88a308461633af5b0a
|
data/LICENSE.txt
CHANGED
@@ -1,21 +1,22 @@
|
|
1
|
-
|
1
|
+
Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved.
|
2
2
|
|
3
|
-
|
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.
|
4
11
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
-
THE SOFTWARE.
|
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.
|
data/README.md
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
# Fiddle
|
2
2
|
|
3
|
-
|
3
|
+
[![Build Status](https://travis-ci.org/ruby/fiddle.svg?branch=master)](https://travis-ci.org/ruby/fiddle)
|
4
4
|
|
5
|
-
|
5
|
+
A libffi wrapper for Ruby.
|
6
|
+
|
7
|
+
Fiddle is an extension to translate a foreign function interface (FFI) with ruby.
|
8
|
+
|
9
|
+
It wraps [libffi](http://sourceware.org/libffi/), a popular C library which provides a portable interface that allows code written in one language to call code written in another language.
|
6
10
|
|
7
11
|
## Installation
|
8
12
|
|
@@ -22,7 +26,72 @@ Or install it yourself as:
|
|
22
26
|
|
23
27
|
## Usage
|
24
28
|
|
25
|
-
|
29
|
+
Here we will use Fiddle::Function to wrap [floor(3) from libm](http://linux.die.net/man/3/floor)
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
require 'fiddle'
|
33
|
+
|
34
|
+
libm = Fiddle.dlopen('/lib/libm.so.6')
|
35
|
+
|
36
|
+
floor = Fiddle::Function.new(
|
37
|
+
libm['floor'],
|
38
|
+
[Fiddle::TYPE_DOUBLE],
|
39
|
+
Fiddle::TYPE_DOUBLE
|
40
|
+
)
|
41
|
+
|
42
|
+
puts floor.call(3.14159) #=> 3.0
|
43
|
+
```
|
44
|
+
|
45
|
+
### Nested Structs
|
46
|
+
|
47
|
+
You can use hashes to create nested structs, where the hash keys are member names and the values are the nested structs:
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
StudentCollegeDetail = struct [
|
51
|
+
'int college_id',
|
52
|
+
'char college_name[50]'
|
53
|
+
]
|
54
|
+
|
55
|
+
StudentDetail = struct [
|
56
|
+
'int id',
|
57
|
+
'char name[20]',
|
58
|
+
{ clg_data: StudentCollegeDetail }
|
59
|
+
]
|
60
|
+
```
|
61
|
+
|
62
|
+
You can also specify an anonymous nested struct, like so:
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
StudentDetail = struct [
|
66
|
+
'int id',
|
67
|
+
'char name[20]',
|
68
|
+
{
|
69
|
+
clg_data: struct([
|
70
|
+
'int college_id',
|
71
|
+
'char college_name[50]'
|
72
|
+
])
|
73
|
+
}
|
74
|
+
]
|
75
|
+
```
|
76
|
+
|
77
|
+
The position of a hash (and the order of the keys in the hash, in the case of a hash with multiple entries), dictate the offsets of the nested struct in memory. The following examples are both syntactically valid but will lay out the structs differently in memory:
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
# order of members in memory: position, id, dimensions
|
81
|
+
Rect = struct [ { position: struct(['float x', 'float y']) },
|
82
|
+
'int id',
|
83
|
+
{ dimensions: struct(['float w', 'float h']) }
|
84
|
+
]
|
85
|
+
|
86
|
+
# order of members in memory: id, position, dimensions
|
87
|
+
Rect = struct [ 'int id',
|
88
|
+
{
|
89
|
+
position: struct(['float x', 'float y']),
|
90
|
+
dimensions: struct(['float w', 'float h'])
|
91
|
+
}
|
92
|
+
]
|
93
|
+
```
|
94
|
+
|
26
95
|
|
27
96
|
## Development
|
28
97
|
|
@@ -32,10 +101,9 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
32
101
|
|
33
102
|
## Contributing
|
34
103
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
104
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/fiddle.
|
36
105
|
|
37
106
|
|
38
107
|
## License
|
39
108
|
|
40
|
-
The gem is available as open source under the terms of the [
|
41
|
-
|
109
|
+
The gem is available as open source under the terms of the [BSD-2-Clause](https://opensource.org/licenses/BSD-2-Clause).
|
data/Rakefile
CHANGED
@@ -1,13 +1,11 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
|
-
require "rake/testtask"
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
t.test_files = FileList['test/**/test_*.rb']
|
3
|
+
desc "Run test"
|
4
|
+
task :test do
|
5
|
+
ruby("test/run.rb")
|
8
6
|
end
|
9
7
|
|
10
8
|
require 'rake/extensiontask'
|
11
9
|
Rake::ExtensionTask.new("fiddle")
|
12
10
|
|
13
|
-
task :default => :test
|
11
|
+
task :default => [:compile, :test]
|
data/bin/downloader.rb
ADDED
@@ -0,0 +1,331 @@
|
|
1
|
+
# Used by configure and make to download or update mirrored Ruby and GCC
|
2
|
+
# files. This will use HTTPS if possible, falling back to HTTP.
|
3
|
+
|
4
|
+
require 'fileutils'
|
5
|
+
require 'open-uri'
|
6
|
+
require 'pathname'
|
7
|
+
begin
|
8
|
+
require 'net/https'
|
9
|
+
rescue LoadError
|
10
|
+
https = 'http'
|
11
|
+
else
|
12
|
+
https = 'https'
|
13
|
+
|
14
|
+
# open-uri of ruby 2.2.0 accept an array of PEMs as ssl_ca_cert, but old
|
15
|
+
# versions are not. so, patching OpenSSL::X509::Store#add_file instead.
|
16
|
+
class OpenSSL::X509::Store
|
17
|
+
alias orig_add_file add_file
|
18
|
+
def add_file(pems)
|
19
|
+
Array(pems).each do |pem|
|
20
|
+
if File.directory?(pem)
|
21
|
+
add_path pem
|
22
|
+
else
|
23
|
+
orig_add_file pem
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
# since open-uri internally checks ssl_ca_cert using File.directory?,
|
29
|
+
# allow to accept an array.
|
30
|
+
class <<File
|
31
|
+
alias orig_directory? directory?
|
32
|
+
def File.directory? files
|
33
|
+
files.is_a?(Array) ? false : orig_directory?(files)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class Downloader
|
39
|
+
def self.https=(https)
|
40
|
+
@@https = https
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.https?
|
44
|
+
@@https == 'https'
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.https
|
48
|
+
@@https
|
49
|
+
end
|
50
|
+
|
51
|
+
class GNU < self
|
52
|
+
def self.download(name, *rest)
|
53
|
+
if https?
|
54
|
+
super("https://raw.githubusercontent.com/gcc-mirror/gcc/master/#{name}", name, *rest)
|
55
|
+
else
|
56
|
+
super("https://repo.or.cz/official-gcc.git/blob_plain/HEAD:/#{name}", name, *rest)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class RubyGems < self
|
62
|
+
def self.download(name, dir = nil, since = true, options = {})
|
63
|
+
require 'rubygems'
|
64
|
+
options = options.dup
|
65
|
+
options[:ssl_ca_cert] = Dir.glob(File.expand_path("../lib/rubygems/ssl_certs/**/*.pem", File.dirname(__FILE__)))
|
66
|
+
super("https://rubygems.org/downloads/#{name}", name, dir, since, options)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
Gems = RubyGems
|
71
|
+
|
72
|
+
class Unicode < self
|
73
|
+
def self.download(name, *rest)
|
74
|
+
super("http://www.unicode.org/Public/#{name}", name, *rest)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.mode_for(data)
|
79
|
+
/\A#!/ =~ data ? 0755 : 0644
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.http_options(file, since)
|
83
|
+
options = {}
|
84
|
+
if since
|
85
|
+
case since
|
86
|
+
when true
|
87
|
+
since = (File.mtime(file).httpdate rescue nil)
|
88
|
+
when Time
|
89
|
+
since = since.httpdate
|
90
|
+
end
|
91
|
+
if since
|
92
|
+
options['If-Modified-Since'] = since
|
93
|
+
end
|
94
|
+
end
|
95
|
+
options['Accept-Encoding'] = '*' # to disable Net::HTTP::GenericRequest#decode_content
|
96
|
+
options
|
97
|
+
end
|
98
|
+
|
99
|
+
# Downloader.download(url, name, [dir, [since]])
|
100
|
+
#
|
101
|
+
# Update a file from url if newer version is available.
|
102
|
+
# Creates the file if the file doesn't yet exist; however, the
|
103
|
+
# directory where the file is being created has to exist already.
|
104
|
+
# The +since+ parameter can take the following values, with associated meanings:
|
105
|
+
# true ::
|
106
|
+
# Take the last-modified time of the current file on disk, and only download
|
107
|
+
# if the server has a file that was modified later. Download unconditionally
|
108
|
+
# if we don't have the file yet. Default.
|
109
|
+
# +some time value+ ::
|
110
|
+
# Use this time value instead of the time of modification of the file on disk.
|
111
|
+
# nil ::
|
112
|
+
# Only download the file if it doesn't exist yet.
|
113
|
+
# false ::
|
114
|
+
# always download url regardless of whether we already have a file,
|
115
|
+
# and regardless of modification times. (This is essentially just a waste of
|
116
|
+
# network resources, except in the case that the file we have is somehow damaged.
|
117
|
+
# Please note that using this recurringly might create or be seen as a
|
118
|
+
# denial of service attack.)
|
119
|
+
#
|
120
|
+
# Example usage:
|
121
|
+
# download 'http://www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt',
|
122
|
+
# 'UnicodeData.txt', 'enc/unicode/data'
|
123
|
+
def self.download(url, name, dir = nil, since = true, options = {})
|
124
|
+
options = options.dup
|
125
|
+
url = URI(url)
|
126
|
+
dryrun = options.delete(:dryrun)
|
127
|
+
if name
|
128
|
+
file = Pathname.new(under(dir, name))
|
129
|
+
else
|
130
|
+
name = File.basename(url.path)
|
131
|
+
end
|
132
|
+
cache_save = options.delete(:cache_save) {
|
133
|
+
ENV["CACHE_SAVE"] != "no"
|
134
|
+
}
|
135
|
+
cache = cache_file(url, name, options.delete(:cache_dir))
|
136
|
+
file ||= cache
|
137
|
+
if since.nil? and file.exist?
|
138
|
+
if $VERBOSE
|
139
|
+
$stdout.puts "#{file} already exists"
|
140
|
+
$stdout.flush
|
141
|
+
end
|
142
|
+
if cache_save
|
143
|
+
save_cache(cache, file, name)
|
144
|
+
end
|
145
|
+
return file.to_path
|
146
|
+
end
|
147
|
+
if dryrun
|
148
|
+
puts "Download #{url} into #{file}"
|
149
|
+
return
|
150
|
+
end
|
151
|
+
if link_cache(cache, file, name, $VERBOSE)
|
152
|
+
return file.to_path
|
153
|
+
end
|
154
|
+
if !https? and URI::HTTPS === url
|
155
|
+
warn "*** using http instead of https ***"
|
156
|
+
url.scheme = 'http'
|
157
|
+
url = URI(url.to_s)
|
158
|
+
end
|
159
|
+
if $VERBOSE
|
160
|
+
$stdout.print "downloading #{name} ... "
|
161
|
+
$stdout.flush
|
162
|
+
end
|
163
|
+
begin
|
164
|
+
data = url.read(options.merge(http_options(file, since.nil? ? true : since)))
|
165
|
+
rescue OpenURI::HTTPError => http_error
|
166
|
+
if http_error.message =~ /^304 / # 304 Not Modified
|
167
|
+
if $VERBOSE
|
168
|
+
$stdout.puts "#{name} not modified"
|
169
|
+
$stdout.flush
|
170
|
+
end
|
171
|
+
return file.to_path
|
172
|
+
end
|
173
|
+
raise
|
174
|
+
rescue Timeout::Error
|
175
|
+
if since.nil? and file.exist?
|
176
|
+
puts "Request for #{url} timed out, using old version."
|
177
|
+
return file.to_path
|
178
|
+
end
|
179
|
+
raise
|
180
|
+
rescue SocketError
|
181
|
+
if since.nil? and file.exist?
|
182
|
+
puts "No network connection, unable to download #{url}, using old version."
|
183
|
+
return file.to_path
|
184
|
+
end
|
185
|
+
raise
|
186
|
+
end
|
187
|
+
mtime = nil
|
188
|
+
dest = (cache_save && cache && !cache.exist? ? cache : file)
|
189
|
+
dest.parent.mkpath
|
190
|
+
dest.open("wb", 0600) do |f|
|
191
|
+
f.write(data)
|
192
|
+
f.chmod(mode_for(data))
|
193
|
+
mtime = data.meta["last-modified"]
|
194
|
+
end
|
195
|
+
if mtime
|
196
|
+
mtime = Time.httpdate(mtime)
|
197
|
+
dest.utime(mtime, mtime)
|
198
|
+
end
|
199
|
+
if $VERBOSE
|
200
|
+
$stdout.puts "done"
|
201
|
+
$stdout.flush
|
202
|
+
end
|
203
|
+
if dest.eql?(cache)
|
204
|
+
link_cache(cache, file, name)
|
205
|
+
elsif cache_save
|
206
|
+
save_cache(cache, file, name)
|
207
|
+
end
|
208
|
+
return file.to_path
|
209
|
+
rescue => e
|
210
|
+
raise "failed to download #{name}\n#{e.message}: #{url}"
|
211
|
+
end
|
212
|
+
|
213
|
+
def self.under(dir, name)
|
214
|
+
dir ? File.join(dir, File.basename(name)) : name
|
215
|
+
end
|
216
|
+
|
217
|
+
def self.cache_file(url, name, cache_dir = nil)
|
218
|
+
case cache_dir
|
219
|
+
when false
|
220
|
+
return nil
|
221
|
+
when nil
|
222
|
+
cache_dir = ENV['CACHE_DIR']
|
223
|
+
if !cache_dir or cache_dir.empty?
|
224
|
+
cache_dir = ".downloaded-cache"
|
225
|
+
end
|
226
|
+
end
|
227
|
+
Pathname.new(cache_dir) + (name || File.basename(URI(url).path))
|
228
|
+
end
|
229
|
+
|
230
|
+
def self.link_cache(cache, file, name, verbose = false)
|
231
|
+
return false unless cache and cache.exist?
|
232
|
+
return true if cache.eql?(file)
|
233
|
+
if /cygwin/ !~ RUBY_PLATFORM or /winsymlink:nativestrict/ =~ ENV['CYGWIN']
|
234
|
+
begin
|
235
|
+
file.make_symlink(cache.relative_path_from(file.parent))
|
236
|
+
rescue SystemCallError
|
237
|
+
else
|
238
|
+
if verbose
|
239
|
+
$stdout.puts "made symlink #{name} to #{cache}"
|
240
|
+
$stdout.flush
|
241
|
+
end
|
242
|
+
return true
|
243
|
+
end
|
244
|
+
end
|
245
|
+
begin
|
246
|
+
file.make_link(cache)
|
247
|
+
rescue SystemCallError
|
248
|
+
else
|
249
|
+
if verbose
|
250
|
+
$stdout.puts "made link #{name} to #{cache}"
|
251
|
+
$stdout.flush
|
252
|
+
end
|
253
|
+
return true
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def self.save_cache(cache, file, name)
|
258
|
+
if cache and !cache.eql?(file) and !cache.exist?
|
259
|
+
begin
|
260
|
+
file.rename(cache)
|
261
|
+
rescue
|
262
|
+
else
|
263
|
+
link_cache(cache, file, name)
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
Downloader.https = https.freeze
|
270
|
+
|
271
|
+
if $0 == __FILE__
|
272
|
+
since = true
|
273
|
+
options = {}
|
274
|
+
until ARGV.empty?
|
275
|
+
case ARGV[0]
|
276
|
+
when '-d'
|
277
|
+
destdir = ARGV[1]
|
278
|
+
ARGV.shift
|
279
|
+
when '-p'
|
280
|
+
# strip directory names from the name to download, and add the
|
281
|
+
# prefix instead.
|
282
|
+
prefix = ARGV[1]
|
283
|
+
ARGV.shift
|
284
|
+
when '-e'
|
285
|
+
since = nil
|
286
|
+
when '-a'
|
287
|
+
since = false
|
288
|
+
when '-n', '--dryrun'
|
289
|
+
options[:dryrun] = true
|
290
|
+
when '--cache-dir'
|
291
|
+
options[:cache_dir] = ARGV[1]
|
292
|
+
ARGV.shift
|
293
|
+
when /\A--cache-dir=(.*)/m
|
294
|
+
options[:cache_dir] = $1
|
295
|
+
when /\A-/
|
296
|
+
abort "#{$0}: unknown option #{ARGV[0]}"
|
297
|
+
else
|
298
|
+
break
|
299
|
+
end
|
300
|
+
ARGV.shift
|
301
|
+
end
|
302
|
+
dl = Downloader.constants.find do |name|
|
303
|
+
ARGV[0].casecmp(name.to_s) == 0
|
304
|
+
end unless ARGV.empty?
|
305
|
+
$VERBOSE = true
|
306
|
+
if dl
|
307
|
+
dl = Downloader.const_get(dl)
|
308
|
+
ARGV.shift
|
309
|
+
ARGV.each do |name|
|
310
|
+
dir = destdir
|
311
|
+
if prefix
|
312
|
+
name = name.sub(/\A\.\//, '')
|
313
|
+
if name.start_with?(destdir+"/")
|
314
|
+
name = name[(destdir.size+1)..-1]
|
315
|
+
if (dir = File.dirname(name)) == '.'
|
316
|
+
dir = destdir
|
317
|
+
else
|
318
|
+
dir = File.join(destdir, dir)
|
319
|
+
end
|
320
|
+
else
|
321
|
+
name = File.basename(name)
|
322
|
+
end
|
323
|
+
name = "#{prefix}/#{name}"
|
324
|
+
end
|
325
|
+
dl.download(name, dir, since, options)
|
326
|
+
end
|
327
|
+
else
|
328
|
+
abort "usage: #{$0} url name" unless ARGV.size == 2
|
329
|
+
Downloader.download(ARGV[0], ARGV[1], destdir, since, options)
|
330
|
+
end
|
331
|
+
end
|