aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorHisham Muhammad <hisham@gobolinux.org>2016-05-22 19:51:45 -0300
committerHisham Muhammad <hisham@gobolinux.org>2016-05-22 19:51:45 -0300
commita3650d51e96aef84657cb29e5f52e570fed828c9 (patch)
tree08a820f19862486b88f39b671803d87f42436abc /src
parent3f6eda258d5c6aee610b095a20beb12e026e6714 (diff)
parent69a6aff477fd794b3b594bc7b9f20775b8e07986 (diff)
downloadluarocks-a3650d51e96aef84657cb29e5f52e570fed828c9.tar.gz
luarocks-a3650d51e96aef84657cb29e5f52e570fed828c9.tar.bz2
luarocks-a3650d51e96aef84657cb29e5f52e570fed828c9.zip
Merge pull request #561 from mpeterv/common-fs-tools-funcs
Move common `luarocks.fs.{unix,win32}.tools` functions into a new module
Diffstat (limited to 'src')
-rw-r--r--src/luarocks/fs.lua5
-rw-r--r--src/luarocks/fs/tools.lua156
-rw-r--r--src/luarocks/fs/unix.lua7
-rw-r--r--src/luarocks/fs/unix/tools.lua151
-rw-r--r--src/luarocks/fs/win32.lua7
-rw-r--r--src/luarocks/fs/win32/tools.lua170
6 files changed, 194 insertions, 302 deletions
diff --git a/src/luarocks/fs.lua b/src/luarocks/fs.lua
index 57302c7f..f3d86a13 100644
--- a/src/luarocks/fs.lua
+++ b/src/luarocks/fs.lua
@@ -68,7 +68,10 @@ load_fns(fs_lua)
68 68
69-- Load platform-specific fallbacks for missing Lua modules 69-- Load platform-specific fallbacks for missing Lua modules
70local ok, fs_plat_tools = pcall(require, "luarocks.fs."..loaded_platform..".tools") 70local ok, fs_plat_tools = pcall(require, "luarocks.fs."..loaded_platform..".tools")
71if ok and fs_plat_tools then load_fns(fs_plat_tools) end 71if ok and fs_plat_tools then
72 load_fns(fs_plat_tools)
73 load_fns(require("luarocks.fs.tools"))
74end
72 75
73 76
74return fs 77return fs
diff --git a/src/luarocks/fs/tools.lua b/src/luarocks/fs/tools.lua
new file mode 100644
index 00000000..ed51b545
--- /dev/null
+++ b/src/luarocks/fs/tools.lua
@@ -0,0 +1,156 @@
1
2--- Common fs operations implemented with third-party tools.
3local tools = {}
4
5local fs = require("luarocks.fs")
6local dir = require("luarocks.dir")
7local cfg = require("luarocks.cfg")
8
9local vars = cfg.variables
10
11local dir_stack = {}
12
13--- Obtain current directory.
14-- Uses the module's internal directory stack.
15-- @return string: the absolute pathname of the current directory.
16function tools.current_dir()
17 local current = cfg.cache_pwd
18 if not current then
19 local pipe = io.popen(fs.quiet_stderr(fs.Q(vars.PWD)))
20 current = pipe:read("*l")
21 pipe:close()
22 cfg.cache_pwd = current
23 end
24 for _, directory in ipairs(dir_stack) do
25 current = fs.absolute_name(directory, current)
26 end
27 return current
28end
29
30--- Change the current directory.
31-- Uses the module's internal directory stack. This does not have exact
32-- semantics of chdir, as it does not handle errors the same way,
33-- but works well for our purposes for now.
34-- @param directory string: The directory to switch to.
35-- @return boolean or (nil, string): true if successful, (nil, error message) if failed.
36function tools.change_dir(directory)
37 assert(type(directory) == "string")
38 if fs.is_dir(directory) then
39 table.insert(dir_stack, directory)
40 return true
41 end
42 return nil, "directory not found: "..directory
43end
44
45--- Change directory to root.
46-- Allows leaving a directory (e.g. for deleting it) in
47-- a crossplatform way.
48function tools.change_dir_to_root()
49 table.insert(dir_stack, "/")
50end
51
52--- Change working directory to the previous in the directory stack.
53function tools.pop_dir()
54 local directory = table.remove(dir_stack)
55 return directory ~= nil
56end
57
58--- Run the given command.
59-- The command is executed in the current directory in the directory stack.
60-- @param cmd string: No quoting/escaping is applied to the command.
61-- @return boolean: true if command succeeds (status code 0), false
62-- otherwise.
63function tools.execute_string(cmd)
64 local current = fs.current_dir()
65 if not current then return false end
66 cmd = fs.command_at(current, cmd)
67 local code = os.execute(cmd)
68 if code == 0 or code == true then
69 return true
70 else
71 return false
72 end
73end
74
75--- Internal implementation function for fs.dir.
76-- Yields a filename on each iteration.
77-- @param at string: directory to list
78-- @return nil
79function tools.dir_iterator(at)
80 local pipe = io.popen(fs.command_at(at, fs.Q(vars.LS)))
81 for file in pipe:lines() do
82 if file ~= "." and file ~= ".." then
83 coroutine.yield(file)
84 end
85 end
86 pipe:close()
87end
88
89--- Download a remote file.
90-- @param url string: URL to be fetched.
91-- @param filename string or nil: this function attempts to detect the
92-- resulting local filename of the remote file as the basename of the URL;
93-- if that is not correct (due to a redirection, for example), the local
94-- filename can be given explicitly as this second argument.
95-- @return (boolean, string): true and the filename on success,
96-- false and the error message on failure.
97function tools.use_downloader(url, filename, cache)
98 assert(type(url) == "string")
99 assert(type(filename) == "string" or not filename)
100
101 filename = fs.absolute_name(filename or dir.base_name(url))
102
103 local ok
104 if cfg.downloader == "wget" then
105 local wget_cmd = fs.Q(vars.WGET).." "..vars.WGETNOCERTFLAG.." --no-cache --user-agent=\""..cfg.user_agent.." via wget\" --quiet "
106 if cfg.connection_timeout and cfg.connection_timeout > 0 then
107 wget_cmd = wget_cmd .. "--timeout="..tonumber(cfg.connection_timeout).." --tries=1 "
108 end
109 if cache then
110 -- --timestamping is incompatible with --output-document,
111 -- but that's not a problem for our use cases.
112 fs.change_dir(dir.dir_name(filename))
113 ok = fs.execute_quiet(wget_cmd.." --timestamping ", url)
114 fs.pop_dir()
115 elseif filename then
116 ok = fs.execute_quiet(wget_cmd.." --output-document ", filename, url)
117 else
118 ok = fs.execute_quiet(wget_cmd, url)
119 end
120 elseif cfg.downloader == "curl" then
121 local curl_cmd = fs.Q(vars.CURL).." "..vars.CURLNOCERTFLAG.." -f -L --user-agent \""..cfg.user_agent.." via curl\" "
122 if cfg.connection_timeout and cfg.connection_timeout > 0 then
123 curl_cmd = curl_cmd .. "--connect-timeout "..tonumber(cfg.connection_timeout).." "
124 end
125 ok = fs.execute_string(fs.quiet_stderr(curl_cmd..fs.Q(url).." > "..fs.Q(filename)))
126 end
127 if ok then
128 return true, filename
129 else
130 return false
131 end
132end
133
134local md5_cmd = {
135 md5sum = fs.Q(vars.MD5SUM),
136 openssl = fs.Q(vars.OPENSSL).." md5",
137 md5 = fs.Q(vars.MD5),
138}
139
140--- Get the MD5 checksum for a file.
141-- @param file string: The file to be computed.
142-- @return string: The MD5 checksum or nil + message
143function tools.get_md5(file)
144 local cmd = md5_cmd[cfg.md5checker]
145 if not cmd then return nil, "no MD5 checker command configured" end
146 local pipe = io.popen(cmd.." "..fs.Q(fs.absolute_name(file)))
147 local computed = pipe:read("*a")
148 pipe:close()
149 if computed then
150 computed = computed:match("("..("%x"):rep(32)..")")
151 end
152 if computed then return computed end
153 return nil, "Failed to compute MD5 hash for file "..tostring(fs.absolute_name(file))
154end
155
156return tools
diff --git a/src/luarocks/fs/unix.lua b/src/luarocks/fs/unix.lua
index e2cc825b..520b3e99 100644
--- a/src/luarocks/fs/unix.lua
+++ b/src/luarocks/fs/unix.lua
@@ -16,6 +16,13 @@ function unix.quiet(cmd)
16 return cmd.." 1> /dev/null 2> /dev/null" 16 return cmd.." 1> /dev/null 2> /dev/null"
17end 17end
18 18
19--- Annotate command string for execution with quiet stderr.
20-- @param cmd string: A command-line string.
21-- @return string: The command-line, with stderr silencing annotation.
22function unix.quiet_stderr(cmd)
23 return cmd.." 2> /dev/null"
24end
25
19--- Return an absolute pathname from a potentially relative one. 26--- Return an absolute pathname from a potentially relative one.
20-- @param pathname string: pathname to convert. 27-- @param pathname string: pathname to convert.
21-- @param relative_to string or nil: path to prepend when making 28-- @param relative_to string or nil: path to prepend when making
diff --git a/src/luarocks/fs/unix/tools.lua b/src/luarocks/fs/unix/tools.lua
index ab55897e..84bd53fd 100644
--- a/src/luarocks/fs/unix/tools.lua
+++ b/src/luarocks/fs/unix/tools.lua
@@ -7,74 +7,16 @@ local fs = require("luarocks.fs")
7local dir = require("luarocks.dir") 7local dir = require("luarocks.dir")
8local cfg = require("luarocks.cfg") 8local cfg = require("luarocks.cfg")
9 9
10local dir_stack = {}
11
12local vars = cfg.variables 10local vars = cfg.variables
13 11
14local function command_at(directory, cmd) 12--- Adds prefix to command to make it run from a directory.
13-- @param directory string: Path to a directory.
14-- @param cmd string: A command-line string.
15-- @return string: The command-line with prefix.
16function tools.command_at(directory, cmd)
15 return "cd " .. fs.Q(fs.absolute_name(directory)) .. " && " .. cmd 17 return "cd " .. fs.Q(fs.absolute_name(directory)) .. " && " .. cmd
16end 18end
17 19
18--- Obtain current directory.
19-- Uses the module's internal directory stack.
20-- @return string: the absolute pathname of the current directory.
21function tools.current_dir()
22 local current = cfg.cache_pwd
23 if not current then
24 local pipe = io.popen(fs.Q(vars.PWD).." 2> /dev/null")
25 current = pipe:read("*l")
26 pipe:close()
27 cfg.cache_pwd = current
28 end
29 for _, directory in ipairs(dir_stack) do
30 current = fs.absolute_name(directory, current)
31 end
32 return current
33end
34
35--- Run the given command.
36-- The command is executed in the current directory in the directory stack.
37-- @param cmd string: No quoting/escaping is applied to the command.
38-- @return boolean: true if command succeeds (status code 0), false
39-- otherwise.
40function tools.execute_string(cmd)
41 local current = fs.current_dir()
42 if not current then return false end
43 local code, err = os.execute(command_at(current, cmd))
44 if code == 0 or code == true then
45 return true
46 else
47 return false
48 end
49end
50
51--- Change the current directory.
52-- Uses the module's internal directory stack. This does not have exact
53-- semantics of chdir, as it does not handle errors the same way,
54-- but works well for our purposes for now.
55-- @param directory string: The directory to switch to.
56function tools.change_dir(directory)
57 assert(type(directory) == "string")
58 if fs.is_dir(directory) then
59 table.insert(dir_stack, directory)
60 return true
61 end
62 return nil, "directory not found: "..directory
63end
64
65--- Change directory to root.
66-- Allows leaving a directory (e.g. for deleting it) in
67-- a crossplatform way.
68function tools.change_dir_to_root()
69 table.insert(dir_stack, "/")
70end
71
72--- Change working directory to the previous in the directory stack.
73function tools.pop_dir()
74 local directory = table.remove(dir_stack)
75 return directory ~= nil
76end
77
78--- Create a directory if it does not already exist. 20--- Create a directory if it does not already exist.
79-- If any of the higher levels in the path name does not exist 21-- If any of the higher levels in the path name does not exist
80-- too, they are created as well. 22-- too, they are created as well.
@@ -155,20 +97,6 @@ function tools.delete(arg)
155 fs.execute_quiet(vars.RM, "-rf", arg) 97 fs.execute_quiet(vars.RM, "-rf", arg)
156end 98end
157 99
158--- Internal implementation function for fs.dir.
159-- Yields a filename on each iteration.
160-- @param at string: directory to list
161-- @return nil
162function tools.dir_iterator(at)
163 local pipe = io.popen(command_at(at, vars.LS))
164 for file in pipe:lines() do
165 if file ~= "." and file ~= ".." then
166 coroutine.yield(file)
167 end
168 end
169 pipe:close()
170end
171
172--- Recursively scan the contents of a directory. 100--- Recursively scan the contents of a directory.
173-- @param at string or nil: directory to scan (will be the current 101-- @param at string or nil: directory to scan (will be the current
174-- directory if none is given). 102-- directory if none is given).
@@ -183,7 +111,7 @@ function tools.find(at)
183 return {} 111 return {}
184 end 112 end
185 local result = {} 113 local result = {}
186 local pipe = io.popen(command_at(at, vars.FIND.." * 2>/dev/null")) 114 local pipe = io.popen(fs.command_at(at, fs.quiet_stderr(vars.FIND.." *")))
187 for file in pipe:lines() do 115 for file in pipe:lines() do
188 table.insert(result, file) 116 table.insert(result, file)
189 end 117 end
@@ -232,51 +160,6 @@ function tools.is_file(file)
232 return fs.execute(vars.TEST, "-f", file) 160 return fs.execute(vars.TEST, "-f", file)
233end 161end
234 162
235--- Download a remote file.
236-- @param url string: URL to be fetched.
237-- @param filename string or nil: this function attempts to detect the
238-- resulting local filename of the remote file as the basename of the URL;
239-- if that is not correct (due to a redirection, for example), the local
240-- filename can be given explicitly as this second argument.
241-- @return (boolean, string): true and the filename on success,
242-- false and the error message on failure.
243function tools.use_downloader(url, filename, cache)
244 assert(type(url) == "string")
245 assert(type(filename) == "string" or not filename)
246
247 filename = fs.absolute_name(filename or dir.base_name(url))
248
249 local ok
250 if cfg.downloader == "wget" then
251 local wget_cmd = fs.Q(vars.WGET).." "..vars.WGETNOCERTFLAG.." --no-cache --user-agent='"..cfg.user_agent.." via wget' --quiet "
252 if cfg.connection_timeout and cfg.connection_timeout > 0 then
253 wget_cmd = wget_cmd .. "--timeout="..tonumber(cfg.connection_timeout).." --tries=1 "
254 end
255 if cache then
256 -- --timestamping is incompatible with --output-document,
257 -- but that's not a problem for our use cases.
258 fs.change_dir(dir.dir_name(filename))
259 ok = fs.execute_quiet(wget_cmd.." --timestamping ", url)
260 fs.pop_dir()
261 elseif filename then
262 ok = fs.execute_quiet(wget_cmd.." --output-document ", filename, url)
263 else
264 ok = fs.execute_quiet(wget_cmd, url)
265 end
266 elseif cfg.downloader == "curl" then
267 local curl_cmd = fs.Q(vars.CURL).." "..vars.CURLNOCERTFLAG.." -f -L --user-agent '"..cfg.user_agent.." via curl' "
268 if cfg.connection_timeout and cfg.connection_timeout > 0 then
269 curl_cmd = curl_cmd .. "--connect-timeout "..tonumber(cfg.connection_timeout).." "
270 end
271 ok = fs.execute_string(curl_cmd..fs.Q(url).." 2> /dev/null 1> "..fs.Q(filename))
272 end
273 if ok then
274 return true, filename
275 else
276 return false
277 end
278end
279
280function tools.chmod(pathname, mode) 163function tools.chmod(pathname, mode)
281 if mode then 164 if mode then
282 return fs.execute(vars.CHMOD, mode, pathname) 165 return fs.execute(vars.CHMOD, mode, pathname)
@@ -319,28 +202,6 @@ function tools.unpack_archive(archive)
319 return true 202 return true
320end 203end
321 204
322local md5_cmd = {
323 md5sum = vars.MD5SUM,
324 openssl = vars.OPENSSL.." md5",
325 md5 = vars.MD5,
326}
327
328--- Get the MD5 checksum for a file.
329-- @param file string: The file to be computed.
330-- @return string: The MD5 checksum
331function tools.get_md5(file)
332 local cmd = md5_cmd[cfg.md5checker]
333 if not cmd then return nil, "no MD5 checker command configured" end
334 local pipe = io.popen(cmd.." "..fs.Q(fs.absolute_name(file)))
335 local computed = pipe:read("*a")
336 pipe:close()
337 if computed then
338 computed = computed:match("("..("%x"):rep(32)..")")
339 end
340 if computed then return computed end
341 return nil, "Failed to compute MD5 hash for file "..tostring(fs.absolute_name(file))
342end
343
344function tools.get_permissions(filename) 205function tools.get_permissions(filename)
345 local pipe = io.popen(vars.STAT.." "..vars.STATFLAG.." "..fs.Q(filename)) 206 local pipe = io.popen(vars.STAT.." "..vars.STATFLAG.." "..fs.Q(filename))
346 local ret = pipe:read("*l") 207 local ret = pipe:read("*l")
diff --git a/src/luarocks/fs/win32.lua b/src/luarocks/fs/win32.lua
index c14c421b..74f3ed69 100644
--- a/src/luarocks/fs/win32.lua
+++ b/src/luarocks/fs/win32.lua
@@ -27,6 +27,13 @@ function win32.quiet(cmd)
27 return cmd.." 2> NUL 1> NUL" 27 return cmd.." 2> NUL 1> NUL"
28end 28end
29 29
30--- Annotate command string for execution with quiet stderr.
31-- @param cmd string: A command-line string.
32-- @return string: The command-line, with stderr silencing annotation.
33function win32.quiet_stderr(cmd)
34 return cmd.." 2> NUL"
35end
36
30local drive_letter = "[%.a-zA-Z]?:?[\\/]" 37local drive_letter = "[%.a-zA-Z]?:?[\\/]"
31 38
32local win_escape_chars = { 39local win_escape_chars = {
diff --git a/src/luarocks/fs/win32/tools.lua b/src/luarocks/fs/win32/tools.lua
index b9dce85c..9cb6d47a 100644
--- a/src/luarocks/fs/win32/tools.lua
+++ b/src/luarocks/fs/win32/tools.lua
@@ -9,22 +9,13 @@ local fs = require("luarocks.fs")
9local dir = require("luarocks.dir") 9local dir = require("luarocks.dir")
10local cfg = require("luarocks.cfg") 10local cfg = require("luarocks.cfg")
11 11
12local dir_stack = {}
13
14local vars = cfg.variables 12local vars = cfg.variables
15 13
16--- Strip the last extension of a filename. 14--- Adds prefix to command to make it run from a directory.
17-- Example: "foo.tar.gz" becomes "foo.tar". 15-- @param directory string: Path to a directory.
18-- If filename has no dots, returns it unchanged. 16-- @param cmd string: A command-line string.
19-- @param filename string: The file name to strip. 17-- @return string: The command-line with prefix.
20-- @return string: The stripped name. 18function tools.command_at(directory, cmd)
21local function strip_extension(filename)
22 assert(type(filename) == "string")
23
24 return (filename:gsub("%.[^.]+$", "")) or filename
25end
26
27local function command_at(directory, cmd)
28 local drive = directory:match("^([A-Za-z]:)") 19 local drive = directory:match("^([A-Za-z]:)")
29 cmd = "cd " .. fs.Q(directory) .. " & " .. cmd 20 cmd = "cd " .. fs.Q(directory) .. " & " .. cmd
30 if drive then 21 if drive then
@@ -33,68 +24,6 @@ local function command_at(directory, cmd)
33 return cmd 24 return cmd
34end 25end
35 26
36--- Obtain current directory.
37-- Uses the module's internal directory stack.
38-- @return string: the absolute pathname of the current directory.
39function tools.current_dir()
40 local current = cfg.cache_pwd
41 if not current then
42 local pipe = io.popen(fs.Q(vars.PWD).. " 2> NUL")
43 current = pipe:read("*l")
44 pipe:close()
45 cfg.cache_pwd = current
46 end
47 for _, directory in ipairs(dir_stack) do
48 current = fs.absolute_name(directory, current)
49 end
50 return current
51end
52
53--- Run the given command.
54-- The command is executed in the current directory in the directory stack.
55-- @param cmd string: No quoting/escaping is applied to the command.
56-- @return boolean: true if command succeeds (status code 0), false
57-- otherwise.
58function tools.execute_string(cmd)
59 local current = fs.current_dir()
60 if not current then return false end
61 cmd = command_at(current, cmd)
62 local code = os.execute(cmd)
63 if code == 0 or code == true then
64 return true
65 else
66 return false
67 end
68end
69
70--- Change the current directory.
71-- Uses the module's internal directory stack. This does not have exact
72-- semantics of chdir, as it does not handle errors the same way,
73-- but works well for our purposes for now.
74-- @param directory string: The directory to switch to.
75-- @return boolean or (nil, string): true if successful, (nil, error message) if failed.
76function tools.change_dir(directory)
77 assert(type(directory) == "string")
78 if fs.is_dir(directory) then
79 table.insert(dir_stack, directory)
80 return true
81 end
82 return nil, "directory not found: "..directory
83end
84
85--- Change directory to root.
86-- Allows leaving a directory (e.g. for deleting it) in
87-- a crossplatform way.
88function tools.change_dir_to_root()
89 table.insert(dir_stack, "/")
90end
91
92--- Change working directory to the previous in the directory stack.
93function tools.pop_dir()
94 local directory = table.remove(dir_stack)
95 return directory ~= nil
96end
97
98--- Create a directory if it does not already exist. 27--- Create a directory if it does not already exist.
99-- If any of the higher levels in the path name does not exist 28-- If any of the higher levels in the path name does not exist
100-- too, they are created as well. 29-- too, they are created as well.
@@ -168,20 +97,6 @@ function tools.delete(arg)
168 fs.execute_quiet("if exist "..fs.Q(arg.."\\").." ( RMDIR /S /Q "..fs.Q(arg).." ) else ( DEL /Q /F "..fs.Q(arg).." )") 97 fs.execute_quiet("if exist "..fs.Q(arg.."\\").." ( RMDIR /S /Q "..fs.Q(arg).." ) else ( DEL /Q /F "..fs.Q(arg).." )")
169end 98end
170 99
171--- Internal implementation function for fs.dir.
172-- Yields a filename on each iteration.
173-- @param at string: directory to list
174-- @return nil
175function tools.dir_iterator(at)
176 local pipe = io.popen(command_at(at, fs.Q(vars.LS)))
177 for file in pipe:lines() do
178 if file ~= "." and file ~= ".." then
179 coroutine.yield(file)
180 end
181 end
182 pipe:close()
183end
184
185--- Recursively scan the contents of a directory. 100--- Recursively scan the contents of a directory.
186-- @param at string or nil: directory to scan (will be the current 101-- @param at string or nil: directory to scan (will be the current
187-- directory if none is given). 102-- directory if none is given).
@@ -196,7 +111,7 @@ function tools.find(at)
196 return {} 111 return {}
197 end 112 end
198 local result = {} 113 local result = {}
199 local pipe = io.popen(command_at(at, fs.Q(vars.FIND).." 2> NUL")) 114 local pipe = io.popen(fs.command_at(at, fs.quiet_stderr(fs.Q(vars.FIND))))
200 for file in pipe:lines() do 115 for file in pipe:lines() do
201 -- Windows find is a bit different 116 -- Windows find is a bit different
202 local first_two = file:sub(1,2) 117 local first_two = file:sub(1,2)
@@ -242,49 +157,14 @@ function tools.is_file(file)
242 return fs.execute(fs.Q(vars.TEST).." -f", file) 157 return fs.execute(fs.Q(vars.TEST).." -f", file)
243end 158end
244 159
245--- Download a remote file. 160--- Strip the last extension of a filename.
246-- @param url string: URL to be fetched. 161-- Example: "foo.tar.gz" becomes "foo.tar".
247-- @param filename string or nil: this function attempts to detect the 162-- If filename has no dots, returns it unchanged.
248-- resulting local filename of the remote file as the basename of the URL; 163-- @param filename string: The file name to strip.
249-- if that is not correct (due to a redirection, for example), the local 164-- @return string: The stripped name.
250-- filename can be given explicitly as this second argument. 165local function strip_extension(filename)
251-- @return (boolean, string): true and the filename on success, 166 assert(type(filename) == "string")
252-- false and the error message on failure. 167 return (filename:gsub("%.[^.]+$", "")) or filename
253function tools.use_downloader(url, filename, cache)
254 assert(type(url) == "string")
255 assert(type(filename) == "string" or not filename)
256
257 filename = fs.absolute_name(filename or dir.base_name(url))
258
259 local ok
260 if cfg.downloader == "wget" then
261 local wget_cmd = fs.Q(vars.WGET).." "..vars.WGETNOCERTFLAG.." --no-cache --user-agent=\""..cfg.user_agent.." via wget\" --quiet "
262 if cfg.connection_timeout and cfg.connection_timeout > 0 then
263 wget_cmd = wget_cmd .. "--timeout="..tonumber(cfg.connection_timeout).." --tries=1 "
264 end
265 if cache then
266 -- --timestamping is incompatible with --output-document,
267 -- but that's not a problem for our use cases.
268 fs.change_dir(dir.dir_name(filename))
269 ok = fs.execute_quiet(wget_cmd.." --timestamping ", url)
270 fs.pop_dir()
271 elseif filename then
272 ok = fs.execute_quiet(wget_cmd.." --output-document ", filename, url)
273 else
274 ok = fs.execute_quiet(wget_cmd, url)
275 end
276 elseif cfg.downloader == "curl" then
277 local curl_cmd = fs.Q(vars.CURL).." "..vars.CURLNOCERTFLAG.." -f -L --user-agent \""..cfg.user_agent.." via curl\" "
278 if cfg.connection_timeout and cfg.connection_timeout > 0 then
279 curl_cmd = curl_cmd .. "--connect-timeout "..tonumber(cfg.connection_timeout).." "
280 end
281 ok = fs.execute_string(curl_cmd..fs.Q(url).." 2> NUL 1> "..fs.Q(filename))
282 end
283 if ok then
284 return true, filename
285 else
286 return false
287 end
288end 168end
289 169
290--- Uncompress gzip file. 170--- Uncompress gzip file.
@@ -334,28 +214,6 @@ function tools.unpack_archive(archive)
334 return true 214 return true
335end 215end
336 216
337local md5_cmd = {
338 md5sum = fs.Q(vars.MD5SUM),
339 openssl = fs.Q(vars.OPENSSL).." md5",
340 md5 = fs.Q(vars.MD5),
341}
342
343--- Get the MD5 checksum for a file.
344-- @param file string: The file to be computed.
345-- @return string: The MD5 checksum or nil + message
346function tools.get_md5(file)
347 local cmd = md5_cmd[cfg.md5checker]
348 if not cmd then return nil, "no MD5 checker command configured" end
349 local pipe = io.popen(cmd.." "..fs.Q(fs.absolute_name(file)))
350 local computed = pipe:read("*a")
351 pipe:close()
352 if computed then
353 computed = computed:match("("..("%x"):rep(32)..")")
354 end
355 if computed then return computed end
356 return nil, "Failed to compute MD5 hash for file "..tostring(fs.absolute_name(file))
357end
358
359--- Test for existance of a file. 217--- Test for existance of a file.
360-- @param file string: filename to test 218-- @param file string: filename to test
361-- @return boolean: true if file exists, false otherwise. 219-- @return boolean: true if file exists, false otherwise.