fiddle 1.0.9 → 1.1.2
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.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/Rakefile +12 -0
- data/ext/fiddle/closure.c +131 -33
- data/ext/fiddle/conversions.c +64 -11
- data/ext/fiddle/conversions.h +2 -0
- data/ext/fiddle/extconf.rb +15 -27
- data/ext/fiddle/fiddle.c +215 -56
- data/ext/fiddle/fiddle.h +44 -6
- data/ext/fiddle/function.c +7 -2
- data/ext/fiddle/handle.c +121 -7
- data/ext/fiddle/memory_view.c +6 -8
- data/ext/fiddle/pointer.c +57 -15
- data/ext/fiddle/win32/libffi-config.rb +5 -5
- data/fiddle.gemspec +1 -8
- data/lib/fiddle/closure.rb +25 -0
- data/lib/fiddle/cparser.rb +28 -14
- data/lib/fiddle/import.rb +2 -0
- data/lib/fiddle/pack.rb +30 -17
- data/lib/fiddle/struct.rb +71 -0
- data/lib/fiddle/value.rb +13 -15
- data/lib/fiddle/version.rb +1 -1
- data/lib/fiddle.rb +34 -1
- metadata +8 -53
- data/bin/downloader.rb +0 -331
- data/bin/extlibs.rb +0 -262
- data/ext/fiddle/extlibs +0 -13
data/lib/fiddle/struct.rb
CHANGED
@@ -13,6 +13,58 @@ module Fiddle
|
|
13
13
|
CStructEntity
|
14
14
|
end
|
15
15
|
|
16
|
+
def self.offsetof(name, members, types) # :nodoc:
|
17
|
+
offset = 0
|
18
|
+
worklist = name.split('.')
|
19
|
+
this_type = self
|
20
|
+
while search_name = worklist.shift
|
21
|
+
index = 0
|
22
|
+
member_index = members.index(search_name)
|
23
|
+
|
24
|
+
unless member_index
|
25
|
+
# Possibly a sub-structure
|
26
|
+
member_index = members.index { |member_name, _|
|
27
|
+
member_name == search_name
|
28
|
+
}
|
29
|
+
return unless member_index
|
30
|
+
end
|
31
|
+
|
32
|
+
types.each { |type, count = 1|
|
33
|
+
orig_offset = offset
|
34
|
+
if type.respond_to?(:entity_class)
|
35
|
+
align = type.alignment
|
36
|
+
type_size = type.size
|
37
|
+
else
|
38
|
+
align = PackInfo::ALIGN_MAP[type]
|
39
|
+
type_size = PackInfo::SIZE_MAP[type]
|
40
|
+
end
|
41
|
+
|
42
|
+
# Unions shouldn't advance the offset
|
43
|
+
if this_type.entity_class == CUnionEntity
|
44
|
+
type_size = 0
|
45
|
+
end
|
46
|
+
|
47
|
+
offset = PackInfo.align(orig_offset, align)
|
48
|
+
|
49
|
+
if worklist.empty?
|
50
|
+
return offset if index == member_index
|
51
|
+
else
|
52
|
+
if index == member_index
|
53
|
+
subtype = types[member_index]
|
54
|
+
members = subtype.members
|
55
|
+
types = subtype.types
|
56
|
+
this_type = subtype
|
57
|
+
break
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
offset += (type_size * count)
|
62
|
+
index += 1
|
63
|
+
}
|
64
|
+
end
|
65
|
+
nil
|
66
|
+
end
|
67
|
+
|
16
68
|
def each
|
17
69
|
return enum_for(__function__) unless block_given?
|
18
70
|
|
@@ -75,6 +127,10 @@ module Fiddle
|
|
75
127
|
def CUnion.entity_class
|
76
128
|
CUnionEntity
|
77
129
|
end
|
130
|
+
|
131
|
+
def self.offsetof(name, members, types) # :nodoc:
|
132
|
+
0
|
133
|
+
end
|
78
134
|
end
|
79
135
|
|
80
136
|
# Wrapper for arrays within a struct
|
@@ -172,6 +228,21 @@ module Fiddle
|
|
172
228
|
define_method(:to_i){ @entity.to_i }
|
173
229
|
define_singleton_method(:types) { types }
|
174
230
|
define_singleton_method(:members) { members }
|
231
|
+
|
232
|
+
# Return the offset of a struct member given its name.
|
233
|
+
# For example:
|
234
|
+
#
|
235
|
+
# MyStruct = struct [
|
236
|
+
# "int64_t i",
|
237
|
+
# "char c",
|
238
|
+
# ]
|
239
|
+
#
|
240
|
+
# MyStruct.offsetof("i") # => 0
|
241
|
+
# MyStruct.offsetof("c") # => 8
|
242
|
+
#
|
243
|
+
define_singleton_method(:offsetof) { |name|
|
244
|
+
klass.offsetof(name, members, types)
|
245
|
+
}
|
175
246
|
members.each{|name|
|
176
247
|
name = name[0] if name.is_a?(Array) # name is a nested struct
|
177
248
|
next if method_defined?(name)
|
data/lib/fiddle/value.rb
CHANGED
@@ -6,17 +6,17 @@ module Fiddle
|
|
6
6
|
def unsigned_value(val, ty)
|
7
7
|
case ty.abs
|
8
8
|
when TYPE_CHAR
|
9
|
-
[val].pack("c").
|
9
|
+
[val].pack("c").unpack1("C")
|
10
10
|
when TYPE_SHORT
|
11
|
-
[val].pack("s!").
|
11
|
+
[val].pack("s!").unpack1("S!")
|
12
12
|
when TYPE_INT
|
13
|
-
[val].pack("i!").
|
13
|
+
[val].pack("i!").unpack1("I!")
|
14
14
|
when TYPE_LONG
|
15
|
-
[val].pack("l!").
|
15
|
+
[val].pack("l!").unpack1("L!")
|
16
16
|
else
|
17
17
|
if defined?(TYPE_LONG_LONG) and
|
18
18
|
ty.abs == TYPE_LONG_LONG
|
19
|
-
[val].pack("q").
|
19
|
+
[val].pack("q").unpack1("Q")
|
20
20
|
else
|
21
21
|
val
|
22
22
|
end
|
@@ -26,17 +26,17 @@ module Fiddle
|
|
26
26
|
def signed_value(val, ty)
|
27
27
|
case ty.abs
|
28
28
|
when TYPE_CHAR
|
29
|
-
[val].pack("C").
|
29
|
+
[val].pack("C").unpack1("c")
|
30
30
|
when TYPE_SHORT
|
31
|
-
[val].pack("S!").
|
31
|
+
[val].pack("S!").unpack1("s!")
|
32
32
|
when TYPE_INT
|
33
|
-
[val].pack("I!").
|
33
|
+
[val].pack("I!").unpack1("i!")
|
34
34
|
when TYPE_LONG
|
35
|
-
[val].pack("L!").
|
35
|
+
[val].pack("L!").unpack1("l!")
|
36
36
|
else
|
37
37
|
if defined?(TYPE_LONG_LONG) and
|
38
38
|
ty.abs == TYPE_LONG_LONG
|
39
|
-
[val].pack("Q").
|
39
|
+
[val].pack("Q").unpack1("q")
|
40
40
|
else
|
41
41
|
val
|
42
42
|
end
|
@@ -80,11 +80,11 @@ module Fiddle
|
|
80
80
|
else
|
81
81
|
case SIZEOF_VOIDP
|
82
82
|
when SIZEOF_LONG
|
83
|
-
return [arg].pack("p").
|
83
|
+
return [arg].pack("p").unpack1("l!")
|
84
84
|
else
|
85
85
|
if defined?(SIZEOF_LONG_LONG) and
|
86
86
|
SIZEOF_VOIDP == SIZEOF_LONG_LONG
|
87
|
-
return [arg].pack("p").
|
87
|
+
return [arg].pack("p").unpack1("q")
|
88
88
|
else
|
89
89
|
raise(RuntimeError, "sizeof(void*)?")
|
90
90
|
end
|
@@ -102,10 +102,8 @@ module Fiddle
|
|
102
102
|
return val.unpack('C*')
|
103
103
|
end
|
104
104
|
end
|
105
|
-
return arg
|
106
|
-
else
|
107
|
-
return arg
|
108
105
|
end
|
106
|
+
return arg
|
109
107
|
else
|
110
108
|
if( arg.respond_to?(:to_ptr) )
|
111
109
|
return arg.to_ptr.to_i
|
data/lib/fiddle/version.rb
CHANGED
data/lib/fiddle.rb
CHANGED
@@ -58,7 +58,36 @@ module Fiddle
|
|
58
58
|
#
|
59
59
|
# See Fiddle::Handle.new for more.
|
60
60
|
def dlopen library
|
61
|
-
|
61
|
+
begin
|
62
|
+
Fiddle::Handle.new(library)
|
63
|
+
rescue DLError => error
|
64
|
+
case RUBY_PLATFORM
|
65
|
+
when /linux/
|
66
|
+
case error.message
|
67
|
+
when /\A(\/.+?): (?:invalid ELF header|file too short)/
|
68
|
+
# This may be a linker script:
|
69
|
+
# https://sourceware.org/binutils/docs/ld.html#Scripts
|
70
|
+
path = $1
|
71
|
+
else
|
72
|
+
raise
|
73
|
+
end
|
74
|
+
else
|
75
|
+
raise
|
76
|
+
end
|
77
|
+
|
78
|
+
File.open(path) do |input|
|
79
|
+
input.each_line do |line|
|
80
|
+
case line
|
81
|
+
when /\A\s*(?:INPUT|GROUP)\s*\(\s*([^\s,\)]+)/
|
82
|
+
# TODO: Should we support multiple files?
|
83
|
+
return dlopen($1)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Not found
|
89
|
+
raise
|
90
|
+
end
|
62
91
|
end
|
63
92
|
module_function :dlopen
|
64
93
|
|
@@ -67,4 +96,8 @@ module Fiddle
|
|
67
96
|
RTLD_GLOBAL = Handle::RTLD_GLOBAL # :nodoc:
|
68
97
|
RTLD_LAZY = Handle::RTLD_LAZY # :nodoc:
|
69
98
|
RTLD_NOW = Handle::RTLD_NOW # :nodoc:
|
99
|
+
|
100
|
+
Fiddle::Types.constants.each do |type|
|
101
|
+
const_set "TYPE_#{type}", Fiddle::Types.const_get(type)
|
102
|
+
end
|
70
103
|
end
|
metadata
CHANGED
@@ -1,58 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fiddle
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aaron Patterson
|
8
8
|
- SHIBATA Hiroshi
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
13
|
-
dependencies:
|
14
|
-
- !ruby/object:Gem::Dependency
|
15
|
-
name: bundler
|
16
|
-
requirement: !ruby/object:Gem::Requirement
|
17
|
-
requirements:
|
18
|
-
- - ">="
|
19
|
-
- !ruby/object:Gem::Version
|
20
|
-
version: '0'
|
21
|
-
type: :development
|
22
|
-
prerelease: false
|
23
|
-
version_requirements: !ruby/object:Gem::Requirement
|
24
|
-
requirements:
|
25
|
-
- - ">="
|
26
|
-
- !ruby/object:Gem::Version
|
27
|
-
version: '0'
|
28
|
-
- !ruby/object:Gem::Dependency
|
29
|
-
name: rake
|
30
|
-
requirement: !ruby/object:Gem::Requirement
|
31
|
-
requirements:
|
32
|
-
- - ">="
|
33
|
-
- !ruby/object:Gem::Version
|
34
|
-
version: '0'
|
35
|
-
type: :development
|
36
|
-
prerelease: false
|
37
|
-
version_requirements: !ruby/object:Gem::Requirement
|
38
|
-
requirements:
|
39
|
-
- - ">="
|
40
|
-
- !ruby/object:Gem::Version
|
41
|
-
version: '0'
|
42
|
-
- !ruby/object:Gem::Dependency
|
43
|
-
name: rake-compiler
|
44
|
-
requirement: !ruby/object:Gem::Requirement
|
45
|
-
requirements:
|
46
|
-
- - ">="
|
47
|
-
- !ruby/object:Gem::Version
|
48
|
-
version: '0'
|
49
|
-
type: :development
|
50
|
-
prerelease: false
|
51
|
-
version_requirements: !ruby/object:Gem::Requirement
|
52
|
-
requirements:
|
53
|
-
- - ">="
|
54
|
-
- !ruby/object:Gem::Version
|
55
|
-
version: '0'
|
12
|
+
date: 2023-11-08 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
56
14
|
description: A libffi wrapper for Ruby.
|
57
15
|
email:
|
58
16
|
- aaron@tenderlovemaking.com
|
@@ -65,15 +23,12 @@ files:
|
|
65
23
|
- LICENSE.txt
|
66
24
|
- README.md
|
67
25
|
- Rakefile
|
68
|
-
- bin/downloader.rb
|
69
|
-
- bin/extlibs.rb
|
70
26
|
- ext/fiddle/closure.c
|
71
27
|
- ext/fiddle/closure.h
|
72
28
|
- ext/fiddle/conversions.c
|
73
29
|
- ext/fiddle/conversions.h
|
74
30
|
- ext/fiddle/depend
|
75
31
|
- ext/fiddle/extconf.rb
|
76
|
-
- ext/fiddle/extlibs
|
77
32
|
- ext/fiddle/fiddle.c
|
78
33
|
- ext/fiddle/fiddle.h
|
79
34
|
- ext/fiddle/function.c
|
@@ -103,7 +58,7 @@ licenses:
|
|
103
58
|
- BSD-2-Clause
|
104
59
|
metadata:
|
105
60
|
msys2_mingw_dependencies: libffi
|
106
|
-
post_install_message:
|
61
|
+
post_install_message:
|
107
62
|
rdoc_options: []
|
108
63
|
require_paths:
|
109
64
|
- lib
|
@@ -111,15 +66,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
111
66
|
requirements:
|
112
67
|
- - ">="
|
113
68
|
- !ruby/object:Gem::Version
|
114
|
-
version: 2.
|
69
|
+
version: 2.5.0
|
115
70
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
116
71
|
requirements:
|
117
72
|
- - ">="
|
118
73
|
- !ruby/object:Gem::Version
|
119
74
|
version: '0'
|
120
75
|
requirements: []
|
121
|
-
rubygems_version: 3.
|
122
|
-
signing_key:
|
76
|
+
rubygems_version: 3.5.0.dev
|
77
|
+
signing_key:
|
123
78
|
specification_version: 4
|
124
79
|
summary: A libffi wrapper for Ruby.
|
125
80
|
test_files: []
|
data/bin/downloader.rb
DELETED
@@ -1,331 +0,0 @@
|
|
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
|