aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorhisham <hisham@9ca3f7c1-7366-0410-b1a3-b5c78f85698c>2009-04-16 22:17:48 +0000
committerhisham <hisham@9ca3f7c1-7366-0410-b1a3-b5c78f85698c>2009-04-16 22:17:48 +0000
commit4f15619eabf3b47853406bbb5b52f5878121df44 (patch)
tree3ed033d2aae9b17b030e8389a945d598ba506404 /src
parentf994a726c346fc1df003debd071a79fa109141f0 (diff)
downloadluarocks-4f15619eabf3b47853406bbb5b52f5878121df44.tar.gz
luarocks-4f15619eabf3b47853406bbb5b52f5878121df44.tar.bz2
luarocks-4f15619eabf3b47853406bbb5b52f5878121df44.zip
Greatly simplified the LuaRocks fs abstractions system
git-svn-id: http://luarocks.org/svn/luarocks/trunk@8 9ca3f7c1-7366-0410-b1a3-b5c78f85698c
Diffstat (limited to 'src')
-rw-r--r--src/luarocks/cfg.lua4
-rw-r--r--src/luarocks/fs.lua43
-rw-r--r--src/luarocks/fs/lua.lua119
-rw-r--r--src/luarocks/fs/unix.lua415
-rw-r--r--src/luarocks/fs/win32.lua267
-rw-r--r--src/luarocks/manif_core.lua1
-rw-r--r--src/luarocks/tools/zip.lua6
7 files changed, 98 insertions, 757 deletions
diff --git a/src/luarocks/cfg.lua b/src/luarocks/cfg.lua
index 71efea47..b667f8e7 100644
--- a/src/luarocks/cfg.lua
+++ b/src/luarocks/cfg.lua
@@ -1,6 +1,6 @@
1 1
2local rawset, next, table, pairs, print, require, io, os, setmetatable = 2local rawset, next, table, pairs, print, require, io, os, setmetatable, program_version =
3 rawset, next, table, pairs, print, require, io, os, setmetatable 3 rawset, next, table, pairs, print, require, io, os, setmetatable, program_version
4 4
5--- Configuration for LuaRocks. 5--- Configuration for LuaRocks.
6-- Tries to load the user's configuration file and 6-- Tries to load the user's configuration file and
diff --git a/src/luarocks/fs.lua b/src/luarocks/fs.lua
index a905755d..d2cb3ddb 100644
--- a/src/luarocks/fs.lua
+++ b/src/luarocks/fs.lua
@@ -1,5 +1,5 @@
1 1
2local rawset = rawset 2local pairs = pairs
3 3
4--- Proxy module for filesystem and platform abstractions. 4--- Proxy module for filesystem and platform abstractions.
5-- All code using "fs" code should require "luarocks.fs", 5-- All code using "fs" code should require "luarocks.fs",
@@ -10,33 +10,30 @@ module("luarocks.fs", package.seeall)
10 10
11local cfg = require("luarocks.cfg") 11local cfg = require("luarocks.cfg")
12 12
13local fs_impl = nil 13local function load_fns(fs_table)
14for _, platform in ipairs(cfg.platforms) do 14 for name, fn in pairs(fs_table) do
15 local ok, result = pcall(require, "luarocks.fs."..platform) 15 if not _M[name] then
16 if ok then 16 _M[name] = fn
17 fs_impl = result
18 if fs_impl then
19 break
20 end 17 end
21 end 18 end
22end 19end
23 20
24local fs_lua = require("luarocks.fs.lua") 21-- Load platform-specific functions
25local fs_unix = require("luarocks.fs.unix") 22local loaded_platform = nil
26 23for _, platform in ipairs(cfg.platforms) do
27local fs_mt = { 24 local ok, fs_plat = pcall(require, "luarocks.fs."..platform)
28 __index = function(t, k) 25 if ok and fs_plat then
29 local impl = fs_lua[k] or fs_impl[k] or fs_unix[k] 26 loaded_platform = platform
30 rawset(t, k, impl) 27 load_fns(fs_plat)
31 return impl 28 break
32 end 29 end
33} 30end
34 31
35setmetatable(luarocks.fs, fs_mt) 32-- Load platform-independent pure-Lua functionality
33local fs_lua = require("luarocks.fs.lua")
34load_fns(fs_lua)
36 35
37fs_lua.init_fs_functions(luarocks.fs) 36-- Load platform-specific fallbacks for missing Lua modules
38fs_unix.init_fs_functions(luarocks.fs) 37local ok, fs_plat_tools = pcall(require, "luarocks.fs."..loaded_platform..".tools")
39if fs_impl then 38if ok and fs_plat_tools then load_fns(fs_plat_tools) end
40 fs_impl.init_fs_functions(luarocks.fs)
41end
42 39
diff --git a/src/luarocks/fs/lua.lua b/src/luarocks/fs/lua.lua
index 7d411e49..9f9a4b63 100644
--- a/src/luarocks/fs/lua.lua
+++ b/src/luarocks/fs/lua.lua
@@ -6,6 +6,8 @@ local assert, type, table, io, package, math, os, ipairs =
6-- using LuaFileSystem, LZLib, MD5 and LuaCurl. 6-- using LuaFileSystem, LZLib, MD5 and LuaCurl.
7module("luarocks.fs.lua", package.seeall) 7module("luarocks.fs.lua", package.seeall)
8 8
9local fs = require("luarocks.fs")
10
9local cfg = require("luarocks.cfg") 11local cfg = require("luarocks.cfg")
10local dir = require("luarocks.dir") 12local dir = require("luarocks.dir")
11 13
@@ -23,36 +25,6 @@ math.randomseed(os.time())
23 25
24dir_separator = "/" 26dir_separator = "/"
25 27
26local fs_absolute_name,
27 fs_copy,
28 fs_current_dir,
29 fs_dir_stack,
30 fs_execute,
31 fs_execute_string,
32 fs_exists,
33 fs_find,
34 fs_is_dir,
35 fs_is_file,
36 fs_make_dir,
37 fs_set_time,
38 fs_Q
39
40function init_fs_functions(impl)
41 fs_absolute_name = impl.absolute_name
42 fs_copy = impl.copy
43 fs_current_dir = impl.current_dir
44 fs_dir_stack = impl.dir_stack
45 fs_execute = impl.execute
46 fs_execute_string = impl.execute_string
47 fs_exists = impl.exists
48 fs_find = impl.find
49 fs_is_dir = impl.is_dir
50 fs_is_file = impl.is_file
51 fs_make_dir = impl.make_dir
52 fs_set_time = impl.set_time
53 fs_Q = impl.Q
54end
55
56--- Quote argument for shell processing. 28--- Quote argument for shell processing.
57-- Adds single quotes and escapes. 29-- Adds single quotes and escapes.
58-- @param arg string: Unquoted argument. 30-- @param arg string: Unquoted argument.
@@ -73,7 +45,7 @@ end
73function is_writable(file) 45function is_writable(file)
74 assert(file) 46 assert(file)
75 local result 47 local result
76 if fs_is_dir(file) then 48 if fs.is_dir(file) then
77 local file2 = file .. '/.tmpluarockstestwritable' 49 local file2 = file .. '/.tmpluarockstestwritable'
78 local fh = io.open(file2, 'w') 50 local fh = io.open(file2, 'w')
79 result = fh ~= nil 51 result = fh ~= nil
@@ -95,31 +67,13 @@ function make_temp_dir(name)
95 assert(type(name) == "string") 67 assert(type(name) == "string")
96 68
97 local temp_dir = (os.getenv("TMP") or "/tmp") .. "/luarocks_" .. name:gsub(dir.separator, "_") .. "-" .. tostring(math.floor(math.random() * 10000)) 69 local temp_dir = (os.getenv("TMP") or "/tmp") .. "/luarocks_" .. name:gsub(dir.separator, "_") .. "-" .. tostring(math.floor(math.random() * 10000))
98 if fs_make_dir(temp_dir) then 70 if fs.make_dir(temp_dir) then
99 return temp_dir 71 return temp_dir
100 else 72 else
101 return nil 73 return nil
102 end 74 end
103end 75end
104 76
105--- Return an absolute pathname from a potentially relative one.
106-- @param pathname string: pathname to convert.
107-- @param relative_to string or nil: path to prepend when making
108-- pathname absolute, or the current dir in the dir stack if
109-- not given.
110-- @return string: The pathname converted to absolute.
111function absolute_name(pathname, relative_to)
112 assert(type(pathname) == "string")
113 assert(type(relative_to) == "string" or not relative_to)
114
115 relative_to = relative_to or fs_current_dir()
116 if pathname:sub(1,1) == "/" then
117 return pathname
118 else
119 return relative_to .. "/" .. pathname
120 end
121end
122
123--- Split protocol and path from an URL or local pathname. 77--- Split protocol and path from an URL or local pathname.
124-- URLs should be in the "protocol://path" format. 78-- URLs should be in the "protocol://path" format.
125-- For local pathnames, "file" is returned as the protocol. 79-- For local pathnames, "file" is returned as the protocol.
@@ -134,11 +88,28 @@ function split_url(url)
134 pathname = url 88 pathname = url
135 end 89 end
136 if protocol == "file" then 90 if protocol == "file" then
137 pathname = fs_absolute_name(pathname) 91 pathname = fs.absolute_name(pathname)
138 end 92 end
139 return protocol, pathname 93 return protocol, pathname
140end 94end
141 95
96--- Run the given command, quoting its arguments.
97-- The command is executed in the current directory in the dir stack.
98-- @param command string: The command to be executed. No quoting/escaping
99-- is applied.
100-- @param ... Strings containing additional arguments, which are quoted.
101-- @return boolean: true if command succeeds (status code 0), false
102-- otherwise.
103function execute(command, ...)
104 assert(type(command) == "string")
105
106 for _, arg in ipairs({...}) do
107 assert(type(arg) == "string")
108 command = command .. " " .. fs.Q(arg)
109 end
110 return fs.execute_string(command)
111end
112
142--------------------------------------------------------------------- 113---------------------------------------------------------------------
143-- LuaFileSystem functions 114-- LuaFileSystem functions
144--------------------------------------------------------------------- 115---------------------------------------------------------------------
@@ -158,23 +129,6 @@ function execute_string(cmd)
158 end 129 end
159end 130end
160 131
161--- Run the given command, quoting its arguments.
162-- The command is executed in the current directory in the dir stack.
163-- @param command string: The command to be executed. No quoting/escaping
164-- is applied.
165-- @param ... Strings containing additional arguments, which are quoted.
166-- @return boolean: true if command succeeds (status code 0), false
167-- otherwise.
168function execute(command, ...)
169 assert(type(command) == "string")
170
171 for _, arg in ipairs({...}) do
172 assert(type(arg) == "string")
173 command = command .. " " .. fs_Q(arg)
174 end
175 return fs_execute_string(command)
176end
177
178--- Obtain current directory. 132--- Obtain current directory.
179-- Uses the module's internal dir stack. 133-- Uses the module's internal dir stack.
180-- @return string: the absolute pathname of the current directory. 134-- @return string: the absolute pathname of the current directory.
@@ -190,7 +144,6 @@ end
190function change_dir(d) 144function change_dir(d)
191 table.insert(dir_stack, lfs.currentdir()) 145 table.insert(dir_stack, lfs.currentdir())
192 lfs.chdir(d) 146 lfs.chdir(d)
193--local x="CHDIR: " for _,d in ipairs(dir_stack) do x=x.." "..d end print(x)
194end 147end
195 148
196--- Change directory to root. 149--- Change directory to root.
@@ -200,14 +153,12 @@ function change_dir_to_root()
200 table.insert(dir_stack, lfs.currentdir()) 153 table.insert(dir_stack, lfs.currentdir())
201 -- TODO Does this work on Windows? 154 -- TODO Does this work on Windows?
202 lfs.chdir("/") 155 lfs.chdir("/")
203--local x="CHDIR ROOT: " for _,d in ipairs(dir_stack) do x=x.." "..d end print(x)
204end 156end
205 157
206--- Change working directory to the previous in the dir stack. 158--- Change working directory to the previous in the dir stack.
207-- @return true if a pop ocurred, false if the stack was empty. 159-- @return true if a pop ocurred, false if the stack was empty.
208function pop_dir() 160function pop_dir()
209 local d = table.remove(dir_stack) 161 local d = table.remove(dir_stack)
210--local x="POP DIR: " for _,d in ipairs(dir_stack) do x=x.." "..d end print(x)
211 if d then 162 if d then
212 lfs.chdir(d) 163 lfs.chdir(d)
213 return true 164 return true
@@ -280,11 +231,11 @@ local function recursive_copy(src, dest)
280 local srcmode = lfs.attributes(src, "mode") 231 local srcmode = lfs.attributes(src, "mode")
281 232
282 if srcmode == "file" then 233 if srcmode == "file" then
283 local ok = fs_copy(src, dest) 234 local ok = fs.copy(src, dest)
284 if not ok then return false end 235 if not ok then return false end
285 elseif srcmode == "directory" then 236 elseif srcmode == "directory" then
286 local subdir = dir.path(dest, dir.base_name(src)) 237 local subdir = dir.path(dest, dir.base_name(src))
287 fs_make_dir(subdir) 238 fs.make_dir(subdir)
288 for file in lfs.dir(src) do 239 for file in lfs.dir(src) do
289 if file ~= "." and file ~= ".." then 240 if file ~= "." and file ~= ".." then
290 local ok = recursive_copy(dir.path(src, file), subdir) 241 local ok = recursive_copy(dir.path(src, file), subdir)
@@ -354,9 +305,9 @@ end
354function list_dir(at) 305function list_dir(at)
355 assert(type(at) == "string" or not at) 306 assert(type(at) == "string" or not at)
356 if not at then 307 if not at then
357 at = fs_current_dir() 308 at = fs.current_dir()
358 end 309 end
359 if not fs_is_dir(at) then 310 if not fs.is_dir(at) then
360 return {} 311 return {}
361 end 312 end
362 local result = {} 313 local result = {}
@@ -392,9 +343,9 @@ end
392function find(at) 343function find(at)
393 assert(type(at) == "string" or not at) 344 assert(type(at) == "string" or not at)
394 if not at then 345 if not at then
395 at = fs_current_dir() 346 at = fs.current_dir()
396 end 347 end
397 if not fs_is_dir(at) then 348 if not fs.is_dir(at) then
398 return {} 349 return {}
399 end 350 end
400 local result = {} 351 local result = {}
@@ -448,7 +399,7 @@ end
448function unzip(zipfile) 399function unzip(zipfile)
449 assert(zipfile) 400 assert(zipfile)
450 -- FIXME!!!! 401 -- FIXME!!!!
451 return fs_execute("unzip", zipfile) 402 return fs.execute("unzip", zipfile)
452end 403end
453 404
454end 405end
@@ -506,7 +457,7 @@ if md5_ok then
506-- @return boolean: true if the MD5 checksum for 'file' equals 'md5sum', false if not 457-- @return boolean: true if the MD5 checksum for 'file' equals 'md5sum', false if not
507-- or if it could not perform the check for any reason. 458-- or if it could not perform the check for any reason.
508function check_md5(file, md5sum) 459function check_md5(file, md5sum)
509 file = fs_absolute_name(file) 460 file = fs.absolute_name(file)
510 local file = io.open(file, "r") 461 local file = io.open(file, "r")
511 if not file then return false end 462 if not file then return false end
512 local computed = md5.sumhexa(file:read("*a")) 463 local computed = md5.sumhexa(file:read("*a"))
@@ -545,13 +496,13 @@ function unpack_archive(archive)
545 496
546 local ok 497 local ok
547 if archive:match("%.tar%.gz$") or archive:match("%.tgz$") then 498 if archive:match("%.tar%.gz$") or archive:match("%.tgz$") then
548 -- ok = fs_execute("tar zxvpf ", archive) 499 -- ok = fs.execute("tar zxvpf ", archive)
549 ok = fs_execute_string("gunzip -c "..archive.."|tar -xf -") 500 ok = fs.execute_string("gunzip -c "..archive.."|tar -xf -")
550 elseif archive:match("%.tar%.bz2$") then 501 elseif archive:match("%.tar%.bz2$") then
551 -- ok = fs_execute("tar jxvpf ", archive) 502 -- ok = fs.execute("tar jxvpf ", archive)
552 ok = fs_execute_string("bunzip2 -c "..archive.."|tar -xf -") 503 ok = fs.execute_string("bunzip2 -c "..archive.."|tar -xf -")
553 elseif archive:match("%.zip$") then 504 elseif archive:match("%.zip$") then
554 ok = fs_execute("unzip ", archive) 505 ok = fs.execute("unzip ", archive)
555 elseif archive:match("%.lua$") or archive:match("%.c$") then 506 elseif archive:match("%.lua$") or archive:match("%.c$") then
556 -- Ignore .lua and .c files; they don't need to be extracted. 507 -- Ignore .lua and .c files; they don't need to be extracted.
557 return true 508 return true
diff --git a/src/luarocks/fs/unix.lua b/src/luarocks/fs/unix.lua
index 1f8411c9..dba152ef 100644
--- a/src/luarocks/fs/unix.lua
+++ b/src/luarocks/fs/unix.lua
@@ -5,389 +5,14 @@ local assert, type, table, io, package, math, os, ipairs =
5--- Unix implementation of filesystem and platform abstractions. 5--- Unix implementation of filesystem and platform abstractions.
6module("luarocks.fs.unix", package.seeall) 6module("luarocks.fs.unix", package.seeall)
7 7
8local fs = require("luarocks.fs")
9
8local cfg = require("luarocks.cfg") 10local cfg = require("luarocks.cfg")
9local dir = require("luarocks.dir") 11local dir = require("luarocks.dir")
12local fs = require("luarocks.fs")
10 13
11math.randomseed(os.time()) 14math.randomseed(os.time())
12 15
13dir_stack = {}
14
15local fs_absolute_name,
16 fs_copy,
17 fs_current_dir,
18 fs_dir_stack,
19 fs_execute,
20 fs_execute_string,
21 fs_is_dir,
22 fs_is_file,
23 fs_make_dir,
24 fs_exists,
25 fs_find,
26 fs_Q
27
28function init_fs_functions(impl)
29 fs_absolute_name = impl.absolute_name
30 fs_copy = impl.copy
31 fs_current_dir = impl.current_dir
32 fs_dir_stack = impl.dir_stack
33 fs_execute = impl.execute
34 fs_execute_string = impl.execute_string
35 fs_is_dir = impl.is_dir
36 fs_is_file = impl.is_file
37 fs_make_dir = impl.make_dir
38 fs_exists = impl.exists
39 fs_find = impl.find
40 fs_Q = impl.Q
41end
42
43--- Quote argument for shell processing.
44-- Adds single quotes and escapes.
45-- @param arg string: Unquoted argument.
46-- @return string: Quoted argument.
47function Q(arg)
48 assert(type(arg) == "string")
49
50 return "'" .. arg:gsub("\\", "\\\\"):gsub("'", "'\\''") .. "'"
51end
52
53--- Obtain current directory.
54-- Uses the module's internal dir stack.
55-- @return string: the absolute pathname of the current directory.
56function current_dir()
57 local current = os.getenv("PWD")
58 if not current then
59 local pipe = io.popen("pwd")
60 current = pipe:read("*l")
61 pipe:close()
62 end
63 for _, d in ipairs(fs_dir_stack) do
64 current = fs_absolute_name(d, current)
65 end
66 return current
67end
68
69--- Run the given command.
70-- The command is executed in the current directory in the dir stack.
71-- @param cmd string: No quoting/escaping is applied to the command.
72-- @return boolean: true if command succeeds (status code 0), false
73-- otherwise.
74function execute_string(cmd)
75 if os.execute("cd " .. fs_Q(fs_current_dir()) .. " && " .. cmd) == 0 then
76 return true
77 else
78 return false
79 end
80end
81
82--- Run the given command, quoting its arguments.
83-- The command is executed in the current directory in the dir stack.
84-- @param command string: The command to be executed. No quoting/escaping
85-- is applied.
86-- @param ... Strings containing additional arguments, which are quoted.
87-- @return boolean: true if command succeeds (status code 0), false
88-- otherwise.
89function execute(command, ...)
90 assert(type(command) == "string")
91
92 for _, arg in ipairs({...}) do
93 assert(type(arg) == "string")
94 command = command .. " " .. fs_Q(arg)
95 end
96 return fs_execute_string(command)
97end
98
99--- Change the current directory.
100-- Uses the module's internal dir stack. This does not have exact
101-- semantics of chdir, as it does not handle errors the same way,
102-- but works well for our purposes for now.
103-- @param d string: The directory to switch to.
104function change_dir(d)
105 assert(type(d) == "string")
106 table.insert(fs_dir_stack, d)
107end
108
109--- Change directory to root.
110-- Allows leaving a directory (e.g. for deleting it) in
111-- a crossplatform way.
112function change_dir_to_root()
113 table.insert(fs_dir_stack, "/")
114end
115
116--- Change working directory to the previous in the dir stack.
117function pop_dir()
118 local d = table.remove(fs_dir_stack)
119 return d ~= nil
120end
121
122--- Create a directory if it does not already exist.
123-- If any of the higher levels in the path name does not exist
124-- too, they are created as well.
125-- @param d string: pathname of directory to create.
126-- @return boolean: true on success, false on failure.
127function make_dir(d)
128 assert(d)
129 return fs_execute("mkdir -p", d)
130end
131
132--- Remove a directory if it is empty.
133-- Does not return errors (for example, if directory is not empty or
134-- if already does not exist)
135-- @param dir string: pathname of directory to remove.
136function remove_dir_if_empty(d)
137 assert(d)
138 fs_execute_string("rmdir "..fs_Q(d).." 1> /dev/null 2> /dev/null")
139end
140
141--- Copy a file.
142-- @param src string: Pathname of source
143-- @param dest string: Pathname of destination
144-- @return boolean or (boolean, string): true on success, false on failure,
145-- plus an error message.
146function copy(src, dest)
147 assert(src and dest)
148 if fs_execute("cp", src, dest) then
149 return true
150 else
151 return false, "Failed copying "..src.." to "..dest
152 end
153end
154
155--- Recursively copy the contents of a directory.
156-- @param src string: Pathname of source
157-- @param dest string: Pathname of destination
158-- @return boolean or (boolean, string): true on success, false on failure,
159-- plus an error message.
160function copy_contents(src, dest)
161 assert(src and dest)
162 if fs_execute_string("cp -pPR "..fs_Q(src).."/* "..fs_Q(dest).." 1> /dev/null 2>/dev/null") then
163 return true
164 else
165 return false, "Failed copying "..src.." to "..dest
166 end
167end
168
169--- Delete a file or a directory and all its contents.
170-- For safety, this only accepts absolute paths.
171-- @param arg string: Pathname of source
172-- @return boolean: true on success, false on failure.
173function delete(arg)
174 assert(arg)
175 assert(arg:sub(1,1) == "/")
176 return fs_execute_string("rm -rf " .. fs_Q(arg) .. " 1> /dev/null 2>/dev/null")
177end
178
179--- List the contents of a directory.
180-- @param at string or nil: directory to list (will be the current
181-- directory if none is given).
182-- @return table: an array of strings with the filenames representing
183-- the contents of a directory.
184function list_dir(at)
185 assert(type(at) == "string" or not at)
186 if not at then
187 at = fs_current_dir()
188 end
189 if not fs_is_dir(at) then
190 return {}
191 end
192 local result = {}
193 local pipe = io.popen("cd "..fs_Q(at).." && ls")
194 for file in pipe:lines() do
195 table.insert(result, file)
196 end
197 pipe:close()
198 return result
199end
200
201--- Recursively scan the contents of a directory.
202-- @param at string or nil: directory to scan (will be the current
203-- directory if none is given).
204-- @return table: an array of strings with the filenames representing
205-- the contents of a directory.
206function find(at)
207 assert(type(at) == "string" or not at)
208 if not at then
209 at = fs_current_dir()
210 end
211 if not fs_is_dir(at) then
212 return {}
213 end
214 local result = {}
215 local pipe = io.popen("cd "..fs_Q(at).." && find * 2>/dev/null")
216 for file in pipe:lines() do
217 table.insert(result, file)
218 end
219 pipe:close()
220 return result
221end
222
223--- Compress files in a .zip archive.
224-- @param zipfile string: pathname of .zip archive to be created.
225-- @param ... Filenames to be stored in the archive are given as
226-- additional arguments.
227-- @return boolean: true on success, false on failure.
228function zip(zipfile, ...)
229 return fs_execute("zip -r", zipfile, ...)
230end
231
232--- Uncompress files from a .zip archive.
233-- @param zipfile string: pathname of .zip archive to be extracted.
234-- @return boolean: true on success, false on failure.
235function unzip(zipfile)
236 assert(zipfile)
237 return fs_execute("unzip", zipfile)
238end
239
240--- Test for existance of a file.
241-- @param file string: filename to test
242-- @return boolean: true if file exists, false otherwise.
243function exists(file)
244 assert(file)
245 return fs_execute("test -r", file)
246end
247
248--- Test is file/dir is writable.
249-- @param file string: filename to test
250-- @return boolean: true if file exists, false otherwise.
251function is_writable(file)
252 assert(file)
253 return fs_execute("test -w", file)
254end
255
256--- Test is pathname is a directory.
257-- @param file string: pathname to test
258-- @return boolean: true if it is a directory, false otherwise.
259function is_dir(file)
260 assert(file)
261 return fs_execute("test -d", file)
262end
263
264--- Test is pathname is a regular file.
265-- @param file string: pathname to test
266-- @return boolean: true if it is a regular file, false otherwise.
267function is_file(file)
268 assert(file)
269 return fs_execute("test -f", file)
270end
271
272--- Download a remote file.
273-- @param url string: URL to be fetched.
274-- @param filename string or nil: this function attempts to detect the
275-- resulting local filename of the remote file as the basename of the URL;
276-- if that is not correct (due to a redirection, for example), the local
277-- filename can be given explicitly as this second argument.
278-- @return boolean: true on success, false on failure.
279function download(url, filename)
280 assert(type(url) == "string")
281 assert(type(filename) == "string" or not filename)
282
283 if cfg.downloader == "wget" then
284 local wget_cmd = "wget --user-agent="..cfg.user_agent.." --quiet --continue "
285 if filename then
286 return fs_execute(wget_cmd.." --output-document ", filename, url)
287 else
288 return fs_execute(wget_cmd, url)
289 end
290 elseif cfg.downloader == "curl" then
291 filename = filename or dir.base_name(url)
292 return fs_execute_string("curl --user-agent "..cfg.user_agent.." "..fs_Q(url).." 2> /dev/null 1> "..fs_Q(filename))
293 end
294end
295
296--- Create a temporary directory.
297-- @param name string: name pattern to use for avoiding conflicts
298-- when creating temporary directory.
299-- @return string or nil: name of temporary directory or nil on failure.
300function make_temp_dir(name)
301 assert(type(name) == "string")
302
303 local temp_dir = (os.getenv("TMP") or "/tmp") .. "/luarocks_" .. name:gsub(dir.separator, "_") .. "-" .. tostring(math.floor(math.random() * 10000))
304 if fs_make_dir(temp_dir) then
305 return temp_dir
306 else
307 return nil
308 end
309end
310
311function chmod(pathname, mode)
312 return fs_execute("chmod "..mode, pathname)
313end
314
315--- Apply a patch.
316-- @param patchname string: The filename of the patch.
317function apply_patch(patchname)
318 return fs_execute("patch -p1 -f -i ", patchname)
319end
320
321--- Unpack an archive.
322-- Extract the contents of an archive, detecting its format by
323-- filename extension.
324-- @param archive string: Filename of archive.
325-- @return boolean or (boolean, string): true on success, false and an error message on failure.
326function unpack_archive(archive)
327 assert(type(archive) == "string")
328
329 local ok
330 if archive:match("%.tar%.gz$") or archive:match("%.tgz$") then
331 -- ok = fs_execute("tar zxvpf ", archive)
332 ok = fs_execute_string("gunzip -c "..archive.."|tar -xf -")
333 elseif archive:match("%.tar%.bz2$") then
334 -- ok = fs_execute("tar jxvpf ", archive)
335 ok = fs_execute_string("bunzip2 -c "..archive.."|tar -xf -")
336 elseif archive:match("%.zip$") then
337 ok = fs_execute("unzip ", archive)
338 elseif archive:match("%.lua$") or archive:match("%.c$") then
339 -- Ignore .lua and .c files; they don't need to be extracted.
340 return true
341 else
342 local ext = archive:match(".*(%..*)")
343 return false, "Unrecognized filename extension "..(ext or "")
344 end
345 if not ok then
346 return false, "Failed extracting "..archive
347 end
348 return true
349end
350
351--- Check the MD5 checksum for a file.
352-- @param file string: The file to be checked.
353-- @param md5sum string: The string with the expected MD5 checksum.
354-- @return boolean: true if the MD5 checksum for 'file' equals 'md5sum', false if not
355-- or if it could not perform the check for any reason.
356function check_md5(file, md5sum)
357 file = fs_absolute_name(file)
358 local computed
359 if cfg.md5checker == "md5sum" then
360 local pipe = io.popen("md5sum "..file)
361 computed = pipe:read("*l"):gsub("[^%x]+", "")
362 pipe:close()
363 if computed then
364 computed = computed:sub(1,32)
365 end
366 elseif cfg.md5checker == "openssl" then
367 local pipe = io.popen("openssl md5 "..file)
368 computed = pipe:read("*l")
369 pipe:close()
370 if computed then
371 computed = computed:sub(-32)
372 end
373 elseif cfg.md5checker == "md5" then
374 local pipe = io.popen("md5 "..file)
375 computed = pipe:read("*l")
376 pipe:close()
377 if computed then
378 computed = computed:sub(-32)
379 end
380 end
381 if not computed then
382 return false
383 end
384 if computed:match("^"..md5sum) then
385 return true
386 else
387 return false
388 end
389end
390
391--- Return an absolute pathname from a potentially relative one. 16--- Return an absolute pathname from a potentially relative one.
392-- @param pathname string: pathname to convert. 17-- @param pathname string: pathname to convert.
393-- @param relative_to string or nil: path to prepend when making 18-- @param relative_to string or nil: path to prepend when making
@@ -398,7 +23,7 @@ function absolute_name(pathname, relative_to)
398 assert(type(pathname) == "string") 23 assert(type(pathname) == "string")
399 assert(type(relative_to) == "string" or not relative_to) 24 assert(type(relative_to) == "string" or not relative_to)
400 25
401 relative_to = relative_to or fs_current_dir() 26 relative_to = relative_to or fs.current_dir()
402 if pathname:sub(1,1) == "/" then 27 if pathname:sub(1,1) == "/" then
403 return pathname 28 return pathname
404 else 29 else
@@ -420,7 +45,7 @@ function split_url(url)
420 pathname = url 45 pathname = url
421 end 46 end
422 if protocol == "file" then 47 if protocol == "file" then
423 pathname = fs_absolute_name(pathname) 48 pathname = fs.absolute_name(pathname)
424 end 49 end
425 return protocol, pathname 50 return protocol, pathname
426end 51end
@@ -446,7 +71,7 @@ function wrap_script(file, dest)
446 wrapper:write('export LUA_PATH LUA_CPATH\n') 71 wrapper:write('export LUA_PATH LUA_CPATH\n')
447 wrapper:write('exec "'..dir.path(cfg.variables["LUA_BINDIR"], cfg.lua_interpreter)..'" -lluarocks.require "'..file..'" "$@"\n') 72 wrapper:write('exec "'..dir.path(cfg.variables["LUA_BINDIR"], cfg.lua_interpreter)..'" -lluarocks.require "'..file..'" "$@"\n')
448 wrapper:close() 73 wrapper:close()
449 if fs_execute("chmod +x",wrapname) then 74 if fs.execute("chmod +x",wrapname) then
450 return true 75 return true
451 else 76 else
452 return nil, "Could not make "..wrapname.." executable." 77 return nil, "Could not make "..wrapname.." executable."
@@ -487,7 +112,33 @@ function is_actual_binary(filename)
487 return false 112 return false
488end 113end
489 114
115function is_actual_binary(filename)
116 if filename:match("%.lua$") then
117 return false
118 end
119 local file = io.open(filename)
120 if file then
121 local found = false
122 local first = file:read()
123 if first:match("#!.*lua") then
124 file:close()
125 return true
126 elseif first:match("#!/bin/sh") then
127 local line = file:read()
128 line = file:read()
129 if not(line and line:match("LUA_PATH")) then
130 file:close()
131 return true
132 end
133 end
134 file:close()
135 else
136 return true
137 end
138 return false
139end
140
490function copy_binary(filename, dest) 141function copy_binary(filename, dest)
491 return fs_copy(filename, dest) 142 return fs.copy(filename, dest)
492end 143end
493 144
diff --git a/src/luarocks/fs/win32.lua b/src/luarocks/fs/win32.lua
index da9b7d60..d005781e 100644
--- a/src/luarocks/fs/win32.lua
+++ b/src/luarocks/fs/win32.lua
@@ -3,25 +3,11 @@
3-- used by this module. 3-- used by this module.
4module("luarocks.fs.win32", package.seeall) 4module("luarocks.fs.win32", package.seeall)
5 5
6local fs = require("luarocks.fs")
7
6local cfg = require("luarocks.cfg") 8local cfg = require("luarocks.cfg")
7local dir = require("luarocks.dir") 9local dir = require("luarocks.dir")
8 10
9local fs_copy,
10 fs_current_dir,
11 fs_execute,
12 fs_execute_string,
13 fs_is_dir,
14 fs_Q
15
16function init_fs_functions(impl)
17 fs_copy = impl.copy
18 fs_current_dir = impl.current_dir
19 fs_execute = impl.execute
20 fs_execute_string = impl.execute_string
21 fs_is_dir = impl.is_dir
22 fs_Q = impl.Q
23end
24
25--- Quote argument for shell processing. Fixes paths on Windows. 11--- Quote argument for shell processing. Fixes paths on Windows.
26-- Adds single quotes and escapes. 12-- Adds single quotes and escapes.
27-- @param arg string: Unquoted argument. 13-- @param arg string: Unquoted argument.
@@ -36,202 +22,6 @@ function Q(arg)
36 return '"' .. arg:gsub('"', '\\"') .. '"' 22 return '"' .. arg:gsub('"', '\\"') .. '"'
37end 23end
38 24
39local function command_at(directory, cmd)
40 local drive = directory:match("^([A-Za-z]:)")
41 cmd = "cd " .. fs_Q(directory) .. " & " .. cmd
42 if drive then
43 cmd = drive .. " & " .. cmd
44 end
45 return cmd
46end
47
48--- Run the given command.
49-- The command is executed in the current directory in the dir stack.
50-- @param cmd string: No quoting/escaping is applied to the command.
51-- @return boolean: true if command succeeds (status code 0), false
52-- otherwise.
53function execute_string(cmd)
54 if os.execute(command_at(fs_current_dir(), cmd)) == 0 then
55 return true
56 else
57 return false
58 end
59end
60
61--- Test for existance of a file.
62-- @param file string: filename to test
63-- @return boolean: true if file exists, false otherwise.
64function exists(file)
65 assert(file)
66 return fs_execute("if not exist " .. fs_Q(file) ..
67 " invalidcommandname 2>NUL 1>NUL")
68end
69
70--- Test is pathname is a directory.
71-- @param file string: pathname to test
72-- @return boolean: true if it is a directory, false otherwise.
73function is_dir(file)
74 assert(file)
75 return fs_execute("chdir /D " .. fs_Q(file) .. " 2>NUL 1>NUL")
76end
77
78--- Test is pathname is a regular file.
79-- @param file string: pathname to test
80-- @return boolean: true if it is a regular file, false otherwise.
81function is_dir(file)
82 assert(file)
83 return fs_execute("test -d" .. fs_Q(file) .. " 2>NUL 1>NUL")
84end
85
86--- Test is file/dir is writable.
87-- @param file string: filename to test
88-- @return boolean: true if file exists, false otherwise.
89function is_writable(file)
90 assert(file)
91 local result
92 if is_dir(file) then
93 local file2 = file .. '/.tmpluarockstestwritable'
94 local fh = io.open(file2, 'w')
95 result = fh ~= nil
96 if fh then fh:close() end
97 os.remove(file2)
98 else
99 local fh = io.open(file, 'r+')
100 result = fh ~= nil
101 if fh then fh:close() end
102 end
103 return result
104end
105
106
107--- Create a directory if it does not already exist.
108-- If any of the higher levels in the path name does not exist
109-- too, they are created as well.
110-- @param d string: pathname of directory to create.
111-- @return boolean: true on success, false on failure.
112function make_dir(d)
113 assert(d)
114 fs_execute("mkdir "..fs_Q(d).." 1> NUL 2> NUL")
115 return 1
116end
117
118--- Remove a directory if it is empty.
119-- Does not return errors (for example, if directory is not empty or
120-- if already does not exist)
121-- @param d string: pathname of directory to remove.
122function remove_dir_if_empty(d)
123 assert(d)
124 fs_execute_string("rmdir "..fs_Q(d).." 1> NUL 2> NUL")
125end
126
127--- Copy a file.
128-- @param src string: Pathname of source
129-- @param dest string: Pathname of destination
130-- @return boolean or (boolean, string): true on success, false on failure,
131-- plus an error message.
132function copy(src, dest)
133 assert(src and dest)
134 if dest:match("[/\\]$") then dest = dest:sub(1, -2) end
135 if fs_execute("cp", src, dest) then
136 return true
137 else
138 return false, "Failed copying "..src.." to "..dest
139 end
140end
141
142--- Recursively copy the contents of a directory.
143-- @param src string: Pathname of source
144-- @param dest string: Pathname of destination
145-- @return boolean or (boolean, string): true on success, false on failure,
146-- plus an error message.
147function copy_contents(src, dest)
148 assert(src and dest)
149 if fs_execute_string("cp -a "..src.."\\*.* "..fs_Q(dest).." 1> NUL 2> NUL") then
150 return true
151 else
152 return false, "Failed copying "..src.." to "..dest
153 end
154end
155
156--- Delete a file or a directory and all its contents.
157-- For safety, this only accepts absolute paths.
158-- @param arg string: Pathname of source
159-- @return boolean: true on success, false on failure.
160function delete(arg)
161 assert(arg)
162 assert(arg:match("^[\a-zA-Z]?:?[\\/]"))
163 fs_execute("chmod a+rw -R ", arg)
164 return fs_execute_string("rm -rf " .. fs_Q(arg) .. " 1> NUL 2> NUL")
165end
166
167--- List the contents of a directory.
168-- @param at string or nil: directory to list (will be the current
169-- directory if none is given).
170-- @return table: an array of strings with the filenames representing
171-- the contents of a directory.
172function list_dir(at)
173 assert(type(at) == "string" or not at)
174 if not at then
175 at = fs_current_dir()
176 end
177 if not fs_is_dir(at) then
178 return {}
179 end
180 local result = {}
181 local pipe = io.popen(command_at(at, "ls"))
182 for file in pipe:lines() do
183 table.insert(result, file)
184 end
185 pipe:close()
186
187 return result
188end
189
190--- Recursively scan the contents of a directory.
191-- @param at string or nil: directory to scan (will be the current
192-- directory if none is given).
193-- @return table: an array of strings with the filenames representing
194-- the contents of a directory. Paths are returned with forward slashes.
195function find(at)
196 assert(type(at) == "string" or not at)
197 if not at then
198 at = fs_current_dir()
199 end
200 if not fs_is_dir(at) then
201 return {}
202 end
203 local result = {}
204 local pipe = io.popen(command_at(at, "find 2> NUL"))
205 for file in pipe:lines() do
206 -- Windows find is a bit different
207 if file:sub(1,2)==".\\" then file=file:sub(3) end
208 if file ~= "." then
209 table.insert(result, (file:gsub("\\", "/")))
210 end
211 end
212 return result
213end
214
215
216--- Download a remote file.
217-- @param url string: URL to be fetched.
218-- @param filename string or nil: this function attempts to detect the
219-- resulting local filename of the remote file as the basename of the URL;
220-- if that is not correct (due to a redirection, for example), the local
221-- filename can be given explicitly as this second argument.
222-- @return boolean: true on success, false on failure.
223function download(url, filename)
224 assert(type(url) == "string")
225 assert(type(filename) == "string" or not filename)
226 local wget_cmd = "wget --user-agent="..cfg.user_agent.." --quiet --continue "
227
228 if filename then
229 return fs_execute(wget_cmd.." --output-document ", filename, url)
230 else
231 return fs_execute(wget_cmd, url)
232 end
233end
234
235--- Strip the last extension of a filename. 25--- Strip the last extension of a filename.
236-- Example: "foo.tar.gz" becomes "foo.tar". 26-- Example: "foo.tar.gz" becomes "foo.tar".
237-- If filename has no dots, returns it unchanged. 27-- If filename has no dots, returns it unchanged.
@@ -243,55 +33,6 @@ local function strip_extension(filename)
243 return (filename:gsub("%.[^.]+$", "")) or filename 33 return (filename:gsub("%.[^.]+$", "")) or filename
244end 34end
245 35
246--- Uncompress gzip file.
247-- @param archive string: Filename of archive.
248-- @return boolean : success status
249local function gunzip(archive)
250 local cmd = fs_execute("gunzip -h 1>NUL 2>NUL") and 'gunzip' or
251 fs_execute("gzip -h 1>NUL 2>NUL") and 'gzip -d'
252 local ok = fs_execute(cmd, archive)
253 return ok
254end
255
256--- Unpack an archive.
257-- Extract the contents of an archive, detecting its format by
258-- filename extension.
259-- @param archive string: Filename of archive.
260-- @return boolean or (boolean, string): true on success, false and an error message on failure.
261function unpack_archive(archive)
262 assert(type(archive) == "string")
263
264 local ok
265 if archive:match("%.tar%.gz$") then
266 ok = gunzip(archive)
267 if ok then
268 ok = fs_execute("tar -xf ", strip_extension(archive))
269 end
270 elseif archive:match("%.tgz$") then
271 ok = gunzip(archive)
272 if ok then
273 ok = fs_execute("tar -xf ", strip_extension(archive)..".tar")
274 end
275 elseif archive:match("%.tar%.bz2$") then
276 ok = fs_execute("bunzip2 ", archive)
277 if ok then
278 ok = fs_execute("tar -xf ", strip_extension(archive))
279 end
280 elseif archive:match("%.zip$") then
281 ok = fs_execute("unzip ", archive)
282 elseif archive:match("%.lua$") or archive:match("%.c$") then
283 -- Ignore .lua and .c files; they don't need to be extracted.
284 return true
285 else
286 local ext = archive:match(".*(%..*)")
287 return false, "Unrecognized filename extension "..(ext or "")
288 end
289 if not ok then
290 return false, "Failed extracting "..archive
291 end
292 return true
293end
294
295--- Return an absolute pathname from a potentially relative one. 36--- Return an absolute pathname from a potentially relative one.
296-- @param pathname string: pathname to convert. 37-- @param pathname string: pathname to convert.
297-- @param relative_to string or nil: path to prepend when making 38-- @param relative_to string or nil: path to prepend when making
@@ -302,7 +43,7 @@ function absolute_name(pathname, relative_to)
302 assert(type(pathname) == "string") 43 assert(type(pathname) == "string")
303 assert(type(relative_to) == "string" or not relative_to) 44 assert(type(relative_to) == "string" or not relative_to)
304 45
305 relative_to = relative_to or fs_current_dir() 46 relative_to = relative_to or fs.current_dir()
306 if pathname:match("^[\.a-zA-Z]?:?[\\/]") then 47 if pathname:match("^[\.a-zA-Z]?:?[\\/]") then
307 return pathname 48 return pathname
308 else 49 else
@@ -344,7 +85,7 @@ function is_actual_binary(name)
344end 85end
345 86
346function copy_binary(filename, dest) 87function copy_binary(filename, dest)
347 local ok, err = fs_copy(filename, dest) 88 local ok, err = fs.copy(filename, dest)
348 if not ok then 89 if not ok then
349 return nil, err 90 return nil, err
350 end 91 end
diff --git a/src/luarocks/manif_core.lua b/src/luarocks/manif_core.lua
index 941c569d..d1eb4ebb 100644
--- a/src/luarocks/manif_core.lua
+++ b/src/luarocks/manif_core.lua
@@ -1,5 +1,6 @@
1 1
2--- Core functions for querying manifest files. 2--- Core functions for querying manifest files.
3-- This module requires no specific 'fs' functionality.
3module("luarocks.manif_core", package.seeall) 4module("luarocks.manif_core", package.seeall)
4 5
5local persist = require("luarocks.persist") 6local persist = require("luarocks.persist")
diff --git a/src/luarocks/tools/zip.lua b/src/luarocks/tools/zip.lua
index 621beae1..18ad967e 100644
--- a/src/luarocks/tools/zip.lua
+++ b/src/luarocks/tools/zip.lua
@@ -209,9 +209,9 @@ function zip(zipfile, ...)
209 209
210 local ok, err 210 local ok, err
211 for _, file in pairs({...}) do 211 for _, file in pairs({...}) do
212 if fs_is_dir(file) then 212 if fs.is_dir(file) then
213 for _, file in pairs(fs_find(file)) do 213 for _, file in pairs(fs.find(file)) do
214 if fs_is_file(file) then 214 if fs.is_file(file) then
215 ok, err = add_to_zip(file) 215 ok, err = add_to_zip(file)
216 if not ok then break end 216 if not ok then break end
217 end 217 end