aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHisham Muhammad <hisham@gobolinux.org>2025-12-28 19:22:31 -0300
committerHisham Muhammad <hisham@gobolinux.org>2025-12-28 19:22:31 -0300
commited96305bc55cd27b7efc191724f29786a7a34026 (patch)
treea44d0ec90a5a8fbbd772a9e58bc7f3950b39ba01
parentf22578d60941e3206ab6a2a0c80aabb70e91731b (diff)
downloadluarocks-ed96305bc55cd27b7efc191724f29786a7a34026.tar.gz
luarocks-ed96305bc55cd27b7efc191724f29786a7a34026.tar.bz2
luarocks-ed96305bc55cd27b7efc191724f29786a7a34026.zip
fs: remove external-tool-based modules
-rw-r--r--src/luarocks/fs/tools.lua222
-rw-r--r--src/luarocks/fs/unix/tools.lua365
-rw-r--r--src/luarocks/fs/win32/tools.lua331
-rw-r--r--vendor/lua-zlib/lua-zlib-1.2-0.rockspec (renamed from vendor/lua-zlib/lua-zlib-1.1-0.rockspec)0
4 files changed, 0 insertions, 918 deletions
diff --git a/src/luarocks/fs/tools.lua b/src/luarocks/fs/tools.lua
deleted file mode 100644
index b7d03759..00000000
--- a/src/luarocks/fs/tools.lua
+++ /dev/null
@@ -1,222 +0,0 @@
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.core.cfg")
8
9local vars = setmetatable({}, { __index = function(_,k) return cfg.variables[k] end })
10
11local dir_stack = {}
12
13do
14 local tool_cache = {}
15
16 local tool_options = {
17 downloader = {
18 desc = "downloader",
19 { var = "WGET", name = "wget" },
20 { var = "CURL", name = "curl" },
21 },
22 md5checker = {
23 desc = "MD5 checker",
24 { var = "MD5SUM", name = "md5sum" },
25 { var = "OPENSSL", name = "openssl", cmdarg = "md5" },
26 { var = "MD5", name = "md5" },
27 },
28 }
29
30 function tools.which_tool(tooltype)
31 local tool = tool_cache[tooltype]
32 local names = {}
33 if not tool then
34 for _, opt in ipairs(tool_options[tooltype]) do
35 table.insert(names, opt.name)
36 if fs.is_tool_available(vars[opt.var], opt.name) then
37 tool = opt
38 tool_cache[tooltype] = opt
39 break
40 end
41 end
42 end
43 if not tool then
44 local tool_names = table.concat(names, ", ", 1, #names - 1) .. " or " .. names[#names]
45 return nil, "no " .. tool_options[tooltype].desc .. " tool available," .. " please install " .. tool_names .. " in your system"
46 end
47 return tool.name, vars[tool.var] .. (tool.cmdarg and " "..tool.cmdarg or "")
48 end
49end
50
51local current_dir_with_cache
52do
53 local cache_pwd
54
55 current_dir_with_cache = function()
56 local current = cache_pwd
57 if not current then
58 local pipe = io.popen(fs.quiet_stderr(vars.PWD))
59 current = pipe:read("*a"):gsub("^%s*", ""):gsub("%s*$", "")
60 pipe:close()
61 cache_pwd = current
62 end
63 for _, directory in ipairs(dir_stack) do
64 current = fs.absolute_name(directory, current)
65 end
66 return current, cache_pwd
67 end
68
69 --- Obtain current directory.
70 -- Uses the module's internal directory stack.
71 -- @return string: the absolute pathname of the current directory.
72 function tools.current_dir()
73 return (current_dir_with_cache()) -- drop second return
74 end
75end
76
77--- Change the current directory.
78-- Uses the module's internal directory stack. This does not have exact
79-- semantics of chdir, as it does not handle errors the same way,
80-- but works well for our purposes for now.
81-- @param directory string: The directory to switch to.
82-- @return boolean or (nil, string): true if successful, (nil, error message) if failed.
83function tools.change_dir(directory)
84 assert(type(directory) == "string")
85 if fs.is_dir(directory) then
86 table.insert(dir_stack, directory)
87 return true
88 end
89 return nil, "directory not found: "..directory
90end
91
92--- Change directory to root.
93-- Allows leaving a directory (e.g. for deleting it) in
94-- a crossplatform way.
95function tools.change_dir_to_root()
96 local curr_dir = fs.current_dir()
97 if not curr_dir or not fs.is_dir(curr_dir) then
98 return false
99 end
100 table.insert(dir_stack, "/")
101 return true
102end
103
104--- Change working directory to the previous in the directory stack.
105function tools.pop_dir()
106 local directory = table.remove(dir_stack)
107 return directory ~= nil
108end
109
110--- Run the given command.
111-- The command is executed in the current directory in the directory stack.
112-- @param cmd string: No quoting/escaping is applied to the command.
113-- @return boolean: true if command succeeds (status code 0), false
114-- otherwise.
115function tools.execute_string(cmd)
116 local current, cache_pwd = current_dir_with_cache()
117 if not current then return false end
118 if current ~= cache_pwd then
119 cmd = fs.command_at(current, cmd)
120 end
121 local code = os.execute(cmd)
122 if code == 0 or code == true then
123 return true
124 else
125 return false
126 end
127end
128
129--- Internal implementation function for fs.dir.
130-- Yields a filename on each iteration.
131-- @param at string: directory to list
132-- @return nil
133function tools.dir_iterator(at)
134 local pipe = io.popen(fs.command_at(at, vars.LS, true))
135 for file in pipe:lines() do
136 if file ~= "." and file ~= ".." then
137 coroutine.yield(file)
138 end
139 end
140 pipe:close()
141end
142
143--- Download a remote file.
144-- @param url string: URL to be fetched.
145-- @param filename string or nil: this function attempts to detect the
146-- resulting local filename of the remote file as the basename of the URL;
147-- if that is not correct (due to a redirection, for example), the local
148-- filename can be given explicitly as this second argument.
149-- @param cache boolean: compare remote timestamps via HTTP HEAD prior to
150-- re-downloading the file.
151-- @return (string, string, string): filename on success,
152-- false and the error message and code on failure.
153function tools.use_downloader(url, filename, cache)
154 assert(type(url) == "string")
155 assert(type(filename) == "string" or not filename)
156
157 filename = fs.absolute_name(filename or dir.base_name(url))
158
159 local downloader, err = fs.which_tool("downloader")
160 if not downloader then
161 return nil, err, "downloader"
162 end
163
164 local ok = false
165 if downloader == "wget" then
166 local wget_cmd = vars.WGET.." "..vars.WGETNOCERTFLAG.." --no-cache --user-agent=\""..cfg.user_agent.." via wget\" --quiet "
167 if cfg.connection_timeout and cfg.connection_timeout > 0 then
168 wget_cmd = wget_cmd .. "--timeout="..tostring(cfg.connection_timeout).." --tries=1 "
169 end
170 if cache then
171 -- --timestamping is incompatible with --output-document,
172 -- but that's not a problem for our use cases.
173 fs.delete(filename .. ".unixtime")
174 fs.change_dir(dir.dir_name(filename))
175 ok = fs.execute_quiet(wget_cmd.." --timestamping ", url)
176 fs.pop_dir()
177 elseif filename then
178 ok = fs.execute_quiet(wget_cmd.." --output-document ", filename, url)
179 else
180 ok = fs.execute_quiet(wget_cmd, url)
181 end
182 elseif downloader == "curl" then
183 local curl_cmd = vars.CURL.." "..vars.CURLNOCERTFLAG.." -f -L --user-agent \""..cfg.user_agent.." via curl\" "
184 if cfg.connection_timeout and cfg.connection_timeout > 0 then
185 curl_cmd = curl_cmd .. "--connect-timeout "..tostring(cfg.connection_timeout).." "
186 end
187 if cache then
188 curl_cmd = curl_cmd .. " -R -z \"" .. filename .. "\" "
189 end
190 ok = fs.execute_string(fs.quiet_stderr(curl_cmd..fs.Q(url).." --output "..fs.Q(filename)))
191 end
192 if ok then
193 return filename
194 else
195 os.remove(filename)
196 return nil, "failed downloading " .. url, "network"
197 end
198end
199
200--- Get the MD5 checksum for a file.
201-- @param file string: The file to be computed.
202-- @return string: The MD5 checksum or nil + message
203function tools.get_md5(file)
204 local ok, md5checker = fs.which_tool("md5checker")
205 if not ok then
206 return false, md5checker
207 end
208
209 local pipe = io.popen(md5checker.." "..fs.Q(fs.absolute_name(file)))
210 local computed = pipe:read("*l")
211 pipe:close()
212 if computed then
213 computed = computed:match("("..("%x"):rep(32)..")")
214 end
215 if computed then
216 return computed
217 else
218 return nil, "Failed to compute MD5 hash for file "..tostring(fs.absolute_name(file))
219 end
220end
221
222return tools
diff --git a/src/luarocks/fs/unix/tools.lua b/src/luarocks/fs/unix/tools.lua
deleted file mode 100644
index 2127aed6..00000000
--- a/src/luarocks/fs/unix/tools.lua
+++ /dev/null
@@ -1,365 +0,0 @@
1
2--- fs operations implemented with third-party tools for Unix platform abstractions.
3local tools = {}
4
5local fs = require("luarocks.fs")
6local dir = require("luarocks.dir")
7local cfg = require("luarocks.core.cfg")
8
9local vars = setmetatable({}, { __index = function(_,k) return cfg.variables[k] end })
10
11--- Adds prefix to command to make it run from a directory.
12-- @param directory string: Path to a directory.
13-- @param cmd string: A command-line string.
14-- @return string: The command-line with prefix.
15function tools.command_at(directory, cmd)
16 return "cd " .. fs.Q(fs.absolute_name(directory)) .. " && " .. cmd
17end
18
19--- Create a directory if it does not already exist.
20-- If any of the higher levels in the path name does not exist
21-- too, they are created as well.
22-- @param directory string: pathname of directory to create.
23-- @return boolean: true on success, false on failure.
24function tools.make_dir(directory)
25 assert(directory)
26 local ok, err = fs.execute(vars.MKDIR.." -p", directory)
27 if not ok then
28 err = "failed making directory "..directory
29 end
30 return ok, err
31end
32
33--- Remove a directory if it is empty.
34-- Does not return errors (for example, if directory is not empty or
35-- if already does not exist)
36-- @param directory string: pathname of directory to remove.
37function tools.remove_dir_if_empty(directory)
38 assert(directory)
39 fs.execute_quiet(vars.RMDIR, directory)
40end
41
42--- Remove a directory if it is empty.
43-- Does not return errors (for example, if directory is not empty or
44-- if already does not exist)
45-- @param directory string: pathname of directory to remove.
46function tools.remove_dir_tree_if_empty(directory)
47 assert(directory)
48 fs.execute_quiet(vars.RMDIR, "-p", directory)
49end
50
51--- Copy a file.
52-- @param src string: Pathname of source
53-- @param dest string: Pathname of destination
54-- @param perm string ("read" or "exec") or nil: Permissions for destination
55-- file or nil to use the source permissions
56-- @return boolean or (boolean, string): true on success, false on failure,
57-- plus an error message.
58function tools.copy(src, dest, perm)
59 assert(src and dest)
60 if fs.execute(vars.CP, src, dest) then
61 if perm then
62 if fs.is_dir(dest) then
63 dest = dir.path(dest, dir.base_name(src))
64 end
65 if fs.set_permissions(dest, perm, "all") then
66 return true
67 else
68 return false, "Failed setting permissions of "..dest
69 end
70 end
71 return true
72 else
73 return false, "Failed copying "..src.." to "..dest
74 end
75end
76
77--- Recursively copy the contents of a directory.
78-- @param src string: Pathname of source
79-- @param dest string: Pathname of destination
80-- @return boolean or (boolean, string): true on success, false on failure,
81-- plus an error message.
82function tools.copy_contents(src, dest)
83 assert(src and dest)
84 if not fs.is_dir(src) then
85 return false, src .. " is not a directory"
86 end
87 if fs.make_dir(dest) and fs.execute_quiet(vars.CP.." -pPR "..fs.Q(src).."/* "..fs.Q(dest)) then
88 return true
89 else
90 return false, "Failed copying "..src.." to "..dest
91 end
92end
93--- Delete a file or a directory and all its contents.
94-- For safety, this only accepts absolute paths.
95-- @param pathname string: Pathname of source
96-- @return true on success, nil and an error on failure
97function tools.delete(pathname)
98 assert(pathname)
99 assert(pathname:sub(1,1) == "/")
100 if fs.execute_quiet(vars.RM, "-rf", pathname) then
101 return true
102 else
103 return nil, "failed deleting " .. pathname
104 end
105end
106
107--- Recursively scan the contents of a directory.
108-- @param at string or nil: directory to scan (will be the current
109-- directory if none is given).
110-- @return table: an array of strings with the filenames representing
111-- the contents of a directory.
112function tools.find(at)
113 assert(type(at) == "string" or not at)
114 if not at then
115 at = fs.current_dir()
116 end
117 if not fs.is_dir(at) then
118 return {}
119 end
120 local result = {}
121 local pipe = io.popen(fs.command_at(at, fs.quiet_stderr(vars.FIND.." *")))
122 for file in pipe:lines() do
123 table.insert(result, file)
124 end
125 pipe:close()
126 return result
127end
128
129--- Compress files in a .zip archive.
130-- @param zipfile string: pathname of .zip archive to be created.
131-- @param ... Filenames to be stored in the archive are given as
132-- additional arguments.
133-- @return boolean: true on success, nil and error message on failure.
134function tools.zip(zipfile, ...)
135 local ok, err = fs.is_tool_available(vars.ZIP, "zip")
136 if not ok then
137 return nil, err
138 end
139 if fs.execute_quiet(vars.ZIP.." -r", zipfile, ...) then
140 return true
141 else
142 return nil, "failed compressing " .. zipfile
143 end
144end
145
146--- Uncompress files from a .zip archive.
147-- @param zipfile string: pathname of .zip archive to be extracted.
148-- @return boolean: true on success, nil and error message on failure.
149function tools.unzip(zipfile)
150 assert(zipfile)
151 local ok, err = fs.is_tool_available(vars.UNZIP, "unzip")
152 if not ok then
153 return nil, err
154 end
155 if fs.execute_quiet(vars.UNZIP, zipfile) then
156 return true
157 else
158 return nil, "failed extracting " .. zipfile
159 end
160end
161
162local function uncompress(default_ext, program, infile, outfile)
163 assert(type(infile) == "string")
164 assert(outfile == nil or type(outfile) == "string")
165 if not outfile then
166 outfile = infile:gsub("%."..default_ext.."$", "")
167 end
168 if fs.execute(fs.Q(program).." -c "..fs.Q(infile).." > "..fs.Q(outfile)) then
169 return true
170 else
171 return nil, "failed extracting " .. infile
172 end
173end
174
175--- Uncompresses a .gz file.
176-- @param infile string: pathname of .gz file to be extracted.
177-- @param outfile string or nil: pathname of output file to be produced.
178-- If not given, name is derived from input file.
179-- @return boolean: true on success; nil and error message on failure.
180function tools.gunzip(infile, outfile)
181 return uncompress("gz", "gunzip", infile, outfile)
182end
183
184--- Uncompresses a .bz2 file.
185-- @param infile string: pathname of .bz2 file to be extracted.
186-- @param outfile string or nil: pathname of output file to be produced.
187-- If not given, name is derived from input file.
188-- @return boolean: true on success; nil and error message on failure.
189function tools.bunzip2(infile, outfile)
190 return uncompress("bz2", "bunzip2", infile, outfile)
191end
192
193do
194 local function rwx_to_octal(rwx)
195 return (rwx:match "r" and 4 or 0)
196 + (rwx:match "w" and 2 or 0)
197 + (rwx:match "x" and 1 or 0)
198 end
199 local umask_cache
200 function tools._unix_umask()
201 if umask_cache then
202 return umask_cache
203 end
204 local fd = assert(io.popen("umask -S"))
205 local umask = assert(fd:read("*a"))
206 fd:close()
207 local u, g, o = umask:match("u=([rwx]*),g=([rwx]*),o=([rwx]*)")
208 if not u then
209 error("invalid umask result")
210 end
211 umask_cache = string.format("%d%d%d",
212 7 - rwx_to_octal(u),
213 7 - rwx_to_octal(g),
214 7 - rwx_to_octal(o))
215 return umask_cache
216 end
217end
218
219--- Set permissions for file or directory
220-- @param filename string: filename whose permissions are to be modified
221-- @param mode string ("read" or "exec"): permissions to set
222-- @param scope string ("user" or "all"): the user(s) to whom the permission applies
223-- @return boolean or (boolean, string): true on success, false on failure,
224-- plus an error message
225function tools.set_permissions(filename, mode, scope)
226 assert(filename and mode and scope)
227
228 local perms, err = fs._unix_mode_scope_to_perms(mode, scope)
229 if err then
230 return false, err
231 end
232
233 return fs.execute(vars.CHMOD, perms, filename)
234end
235
236function tools.browser(url)
237 return fs.execute(cfg.web_browser, url)
238end
239
240-- Set access and modification times for a file.
241-- @param filename File to set access and modification times for.
242-- @param time may be a string or number containing the format returned
243-- by os.time, or a table ready to be processed via os.time; if
244-- nil, current time is assumed.
245function tools.set_time(file, time)
246 assert(time == nil or type(time) == "table" or type(time) == "number")
247 file = dir.normalize(file)
248 local flag = ""
249 if type(time) == "number" then
250 time = os.date("*t", time)
251 end
252 if type(time) == "table" then
253 flag = ("-t %04d%02d%02d%02d%02d.%02d"):format(time.year, time.month, time.day, time.hour, time.min, time.sec)
254 end
255 return fs.execute(vars.TOUCH .. " " .. flag, file)
256end
257
258--- Create a temporary directory.
259-- @param name_pattern string: name pattern to use for avoiding conflicts
260-- when creating temporary directory.
261-- @return string or (nil, string): name of temporary directory or (nil, error message) on failure.
262function tools.make_temp_dir(name_pattern)
263 assert(type(name_pattern) == "string")
264 name_pattern = dir.normalize(name_pattern)
265
266 local template = (os.getenv("TMPDIR") or "/tmp") .. "/luarocks_" .. name_pattern:gsub("/", "_") .. "-XXXXXX"
267 local pipe = io.popen(vars.MKTEMP.." -d "..fs.Q(template))
268 local dirname = pipe:read("*l")
269 pipe:close()
270 if dirname and dirname:match("^/") then
271 return dirname
272 end
273 return nil, "Failed to create temporary directory "..tostring(dirname)
274end
275
276--- Test is file/directory exists
277-- @param file string: filename to test
278-- @return boolean: true if file exists, false otherwise.
279function tools.exists(file)
280 assert(file)
281 return fs.execute(vars.TEST, "-e", file)
282end
283
284--- Test is pathname is a directory.
285-- @param file string: pathname to test
286-- @return boolean: true if it is a directory, false otherwise.
287function tools.is_dir(file)
288 assert(file)
289 return fs.execute(vars.TEST, "-d", file)
290end
291
292--- Test is pathname is a regular file.
293-- @param file string: pathname to test
294-- @return boolean: true if it is a regular file, false otherwise.
295function tools.is_file(file)
296 assert(file)
297 return fs.execute(vars.TEST, "-f", file)
298end
299
300function tools.current_user()
301 local user = os.getenv("USER")
302 if user then
303 return user
304 end
305 local pd = io.popen("whoami", "r")
306 if not pd then
307 return ""
308 end
309 user = pd:read("*l")
310 pd:close()
311 return user
312end
313
314function tools.is_superuser()
315 return fs.current_user() == "root"
316end
317
318local lock_mt = {
319 __gc = function(lock)
320 fs.unlock_access(lock)
321 end
322}
323
324function tools.lock_access(dirname, force)
325 local ok, err = fs.make_dir(dirname)
326 if not ok then
327 return nil, err
328 end
329
330 local tempfile = dir.path(dirname, ".lock.tmp." .. tostring(math.random(100000000)))
331
332 local fd, fderr = io.open(tempfile, "w")
333 if not fd then
334 return nil, "failed opening temp file " .. tempfile .. " for locking: " .. fderr
335 end
336
337 local ok, werr = fd:write("lock file for " .. dirname)
338 if not ok then
339 return nil, "failed writing temp file " .. tempfile .. " for locking: " .. werr
340 end
341
342 fd:close()
343
344 local lockfile = dir.path(dirname, "lockfile.lfs")
345
346 local force_flag = force and " -f" or ""
347
348 if fs.execute(vars.LN .. force_flag, tempfile, lockfile) then
349 local lock = {
350 tempfile = tempfile,
351 lockfile = lockfile,
352 }
353 setmetatable(lock, lock_mt)
354 return lock
355 else
356 return nil, "File exists" -- same message as luafilesystem
357 end
358end
359
360function tools.unlock_access(lock)
361 os.remove(lock.lockfile)
362 os.remove(lock.tempfile)
363end
364
365return tools
diff --git a/src/luarocks/fs/win32/tools.lua b/src/luarocks/fs/win32/tools.lua
deleted file mode 100644
index 56f04b19..00000000
--- a/src/luarocks/fs/win32/tools.lua
+++ /dev/null
@@ -1,331 +0,0 @@
1
2--- fs operations implemented with third-party tools for Windows platform abstractions.
3-- Download http://unxutils.sourceforge.net/ for Windows GNU utilities
4-- used by this module.
5local tools = {}
6
7local fs = require("luarocks.fs")
8local dir = require("luarocks.dir")
9local cfg = require("luarocks.core.cfg")
10
11local vars = setmetatable({}, { __index = function(_,k) return cfg.variables[k] end })
12
13local dir_sep = package.config:sub(1, 1)
14
15--- Adds prefix to command to make it run from a directory.
16-- @param directory string: Path to a directory.
17-- @param cmd string: A command-line string.
18-- @param exit_on_error bool: Exits immediately if entering the directory failed.
19-- @return string: The command-line with prefix.
20function tools.command_at(directory, cmd, exit_on_error)
21 local drive = directory:match("^([A-Za-z]:)")
22 local op = " & "
23 if exit_on_error then
24 op = " && "
25 end
26 local cmd_prefixed = "cd " .. fs.Q(directory) .. op .. cmd
27 if drive then
28 cmd_prefixed = drive .. " & " .. cmd_prefixed
29 end
30 return cmd_prefixed
31end
32
33--- Create a directory if it does not already exist.
34-- If any of the higher levels in the path name does not exist
35-- too, they are created as well.
36-- @param directory string: pathname of directory to create.
37-- @return boolean: true on success, false on failure.
38function tools.make_dir(directory)
39 assert(directory)
40 directory = dir.normalize(directory)
41 fs.execute_quiet(vars.MKDIR, directory)
42 if not fs.is_dir(directory) then
43 return false, "failed making directory "..directory
44 end
45 return true
46end
47
48--- Remove a directory if it is empty.
49-- Does not return errors (for example, if directory is not empty or
50-- if already does not exist)
51-- @param directory string: pathname of directory to remove.
52function tools.remove_dir_if_empty(directory)
53 assert(directory)
54 fs.execute_quiet(vars.RMDIR, directory)
55end
56
57--- Remove a directory if it is empty.
58-- Does not return errors (for example, if directory is not empty or
59-- if already does not exist)
60-- @param directory string: pathname of directory to remove.
61function tools.remove_dir_tree_if_empty(directory)
62 assert(directory)
63 while true do
64 fs.execute_quiet(vars.RMDIR, directory)
65 local parent = dir.dir_name(directory)
66 if parent ~= directory then
67 directory = parent
68 else
69 break
70 end
71 end
72end
73
74--- Copy a file.
75-- @param src string: Pathname of source
76-- @param dest string: Pathname of destination
77-- @return boolean or (boolean, string): true on success, false on failure,
78-- plus an error message.
79function tools.copy(src, dest)
80 assert(src and dest)
81 if dest:match("[/\\]$") then dest = dest:sub(1, -2) end
82 local ok = fs.execute(vars.CP, src, dest)
83 if ok then
84 return true
85 else
86 return false, "Failed copying "..src.." to "..dest
87 end
88end
89
90--- Recursively copy the contents of a directory.
91-- @param src string: Pathname of source
92-- @param dest string: Pathname of destination
93-- @return boolean or (boolean, string): true on success, false on failure,
94-- plus an error message.
95function tools.copy_contents(src, dest)
96 assert(src and dest)
97 if not fs.is_dir(src) then
98 return false, src .. " is not a directory"
99 end
100 if fs.make_dir(dest) and fs.execute_quiet(vars.CP, "-dR", src.."\\*.*", dest) then
101 return true
102 else
103 return false, "Failed copying "..src.." to "..dest
104 end
105end
106
107--- Delete a file or a directory and all its contents.
108-- For safety, this only accepts absolute paths.
109-- @param arg string: Pathname of source
110-- @return nil
111function tools.delete(arg)
112 assert(arg)
113 assert(arg:match("^[a-zA-Z]?:?[\\/]"))
114 fs.execute_quiet("if exist "..fs.Q(arg.."\\*").." ( RMDIR /S /Q "..fs.Q(arg).." ) else ( DEL /Q /F "..fs.Q(arg).." )")
115end
116
117--- Recursively scan the contents of a directory.
118-- @param at string or nil: directory to scan (will be the current
119-- directory if none is given).
120-- @return table: an array of strings with the filenames representing
121-- the contents of a directory. Paths are returned with forward slashes.
122function tools.find(at)
123 assert(type(at) == "string" or not at)
124 if not at then
125 at = fs.current_dir()
126 end
127 if not fs.is_dir(at) then
128 return {}
129 end
130 local result = {}
131 local pipe = io.popen(fs.command_at(at, fs.quiet_stderr(vars.FIND), true))
132 for file in pipe:lines() do
133 -- Windows find is a bit different
134 local file = file
135 local first_two = file:sub(1,2)
136 if first_two == ".\\" or first_two == "./" then file=file:sub(3) end
137 if file ~= "." then
138 table.insert(result, (file:gsub("[\\/]", dir_sep)))
139 end
140 end
141 pipe:close()
142 return result
143end
144
145--- Compress files in a .zip archive.
146-- @param zipfile string: pathname of .zip archive to be created.
147-- @param ... Filenames to be stored in the archive are given as
148-- additional arguments.
149-- @return boolean: true on success, nil and error message on failure.
150function tools.zip(zipfile, ...)
151 if fs.execute_quiet(vars.SEVENZ.." -aoa a -tzip", zipfile, ...) then
152 return true
153 else
154 return nil, "failed compressing " .. zipfile
155 end
156end
157
158--- Uncompress files from a .zip archive.
159-- @param zipfile string: pathname of .zip archive to be extracted.
160-- @return boolean: true on success, nil and error message on failure.
161function tools.unzip(zipfile)
162 assert(zipfile)
163 if fs.execute_quiet(vars.SEVENZ.." -aoa x", zipfile) then
164 return true
165 else
166 return nil, "failed extracting " .. zipfile
167 end
168end
169
170local function sevenz(default_ext, infile, outfile)
171 assert(type(infile) == "string")
172 assert(outfile == nil or type(outfile) == "string")
173
174 local dropext = infile:gsub("%."..default_ext.."$", "")
175 local outdir = dir.dir_name(dropext)
176
177 infile = fs.absolute_name(infile)
178
179 local cmdline = vars.SEVENZ.." -aoa -t* -o"..fs.Q(outdir).." x "..fs.Q(infile)
180 local ok, err = fs.execute_quiet(cmdline)
181 if not ok then
182 return nil, "failed extracting " .. infile
183 end
184
185 if outfile then
186 outfile = fs.absolute_name(outfile)
187 dropext = fs.absolute_name(dropext)
188 ok, err = os.rename(dropext, outfile)
189 if not ok then
190 return nil, "failed creating new file " .. outfile
191 end
192 end
193
194 return true
195end
196
197--- Uncompresses a .gz file.
198-- @param infile string: pathname of .gz file to be extracted.
199-- @param outfile string or nil: pathname of output file to be produced.
200-- If not given, name is derived from input file.
201-- @return boolean: true on success; nil and error message on failure.
202function tools.gunzip(infile, outfile)
203 return sevenz("gz", infile, outfile)
204end
205
206--- Uncompresses a .bz2 file.
207-- @param infile string: pathname of .bz2 file to be extracted.
208-- @param outfile string or nil: pathname of output file to be produced.
209-- If not given, name is derived from input file.
210-- @return boolean: true on success; nil and error message on failure.
211function tools.bunzip2(infile, outfile)
212 return sevenz("bz2", infile, outfile)
213end
214
215--- Helper function for fs.set_permissions
216-- @return table: an array of all system users
217local function get_system_users()
218 local exclude = {
219 [""] = true,
220 ["Name"] = true,
221 ["\128\164\172\168\173\168\225\226\224\160\226\174\224"] = true, -- Administrator in cp866
222 ["Administrator"] = true,
223 }
224 local result = {}
225 local fd = assert(io.popen("wmic UserAccount get name"))
226 for user in fd:lines() do
227 local user = user:gsub("%s+$", "")
228 if not exclude[user] then
229 table.insert(result, user)
230 end
231 end
232 return result
233end
234
235--- Set permissions for file or directory
236-- @param filename string: filename whose permissions are to be modified
237-- @param mode string ("read" or "exec"): permission to set
238-- @param scope string ("user" or "all"): the user(s) to whom the permission applies
239-- @return boolean or (boolean, string): true on success, false on failure,
240-- plus an error message
241function tools.set_permissions(filename, mode, scope)
242 assert(filename and mode and scope)
243
244 if scope == "user" then
245 local perms
246 if mode == "read" then
247 perms = "(R,W,M)"
248 elseif mode == "exec" then
249 perms = "(F)"
250 end
251
252 local ok
253 -- Take ownership of the given file
254 ok = fs.execute_quiet("takeown /f " .. fs.Q(filename))
255 if not ok then
256 return false, "Could not take ownership of the given file"
257 end
258 local username = os.getenv('USERNAME')
259 -- Grant the current user the proper rights
260 ok = fs.execute_quiet(vars.ICACLS .. " " .. fs.Q(filename) .. " /inheritance:d /grant:r " .. fs.Q(username) .. ":" .. perms)
261 if not ok then
262 return false, "Failed setting permission " .. mode .. " for " .. scope
263 end
264 -- Finally, remove all the other users from the ACL in order to deny them access to the file
265 for _, user in pairs(get_system_users()) do
266 if username ~= user then
267 local ok = fs.execute_quiet(vars.ICACLS .. " " .. fs.Q(filename) .. " /remove " .. fs.Q(user))
268 if not ok then
269 return false, "Failed setting permission " .. mode .. " for " .. scope
270 end
271 end
272 end
273 elseif scope == "all" then
274 local my_perms, others_perms
275 if mode == "read" then
276 my_perms = "(R,W,M)"
277 others_perms = "(R)"
278 elseif mode == "exec" then
279 my_perms = "(F)"
280 others_perms = "(RX)"
281 end
282
283 local ok
284 -- Grant permissions available to all users
285 ok = fs.execute_quiet(vars.ICACLS .. " " .. fs.Q(filename) .. " /inheritance:d /grant:r *S-1-1-0:" .. others_perms)
286 if not ok then
287 return false, "Failed setting permission " .. mode .. " for " .. scope
288 end
289
290 -- Grant permissions available only to the current user
291 ok = fs.execute_quiet(vars.ICACLS .. " " .. fs.Q(filename) .. " /inheritance:d /grant \"%USERNAME%\":" .. my_perms)
292
293 -- This may not be necessary if the above syntax is correct,
294 -- but I couldn't really test the extra quotes above, so if that
295 -- fails we try again with the syntax used in previous releases
296 -- just to be on the safe side
297 if not ok then
298 ok = fs.execute_quiet(vars.ICACLS .. " " .. fs.Q(filename) .. " /inheritance:d /grant %USERNAME%:" .. my_perms)
299 end
300
301 if not ok then
302 return false, "Failed setting permission " .. mode .. " for " .. scope
303 end
304 end
305
306 return true
307end
308
309function tools.browser(url)
310 return fs.execute(cfg.web_browser..' "Starting docs..." '..fs.Q(url))
311end
312
313-- Set access and modification times for a file.
314-- @param filename File to set access and modification times for.
315-- @param time may be a string or number containing the format returned
316-- by os.time, or a table ready to be processed via os.time; if
317-- nil, current time is assumed.
318function tools.set_time(filename, time)
319 return true -- FIXME
320end
321
322function tools.lock_access(dirname)
323 -- NYI
324 return {}
325end
326
327function tools.unlock_access(lock)
328 -- NYI
329end
330
331return tools
diff --git a/vendor/lua-zlib/lua-zlib-1.1-0.rockspec b/vendor/lua-zlib/lua-zlib-1.2-0.rockspec
index 7927d6ce..7927d6ce 100644
--- a/vendor/lua-zlib/lua-zlib-1.1-0.rockspec
+++ b/vendor/lua-zlib/lua-zlib-1.2-0.rockspec