Tamar 0.7.2 → 0.7.3
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY +19 -2
- data/Rakefile +5 -0
- data/Tamar.gemspec +27 -2
- data/VERSION +1 -1
- data/src/luadist/CMakeLists.txt +42 -0
- data/src/luadist/COPYRIGHT +34 -0
- data/src/luadist/README +95 -0
- data/src/luadist/dist.cmake +436 -0
- data/src/luadist/dist.info +20 -0
- data/src/luadist/dist/config.lua.in +108 -0
- data/src/luadist/dist/dep.lua +283 -0
- data/src/luadist/dist/fetch.lua +234 -0
- data/src/luadist/dist/init.lua +616 -0
- data/src/luadist/dist/log.lua +37 -0
- data/src/luadist/dist/manifest.lua +204 -0
- data/src/luadist/dist/package.lua +271 -0
- data/src/luadist/dist/persist.lua +132 -0
- data/src/luadist/dist/sys.lua +436 -0
- data/src/luadist/doc/index.html +147 -0
- data/src/luadist/doc/luadoc.css +286 -0
- data/src/luadist/doc/modules/dist.dep.html +489 -0
- data/src/luadist/doc/modules/dist.fetch.html +197 -0
- data/src/luadist/doc/modules/dist.html +547 -0
- data/src/luadist/doc/modules/dist.log.html +187 -0
- data/src/luadist/doc/modules/dist.manifest.html +235 -0
- data/src/luadist/doc/modules/dist.package.html +323 -0
- data/src/luadist/doc/modules/dist.persist.html +345 -0
- data/src/luadist/doc/modules/dist.sys.html +1058 -0
- data/src/luadist/luadist +443 -0
- metadata +27 -2
@@ -0,0 +1,132 @@
|
|
1
|
+
--- LuaDist simple persistance functions
|
2
|
+
-- Peter Drahoš, LuaDist Project, 2010
|
3
|
+
-- Original Code borrowed from LuaRocks Project
|
4
|
+
|
5
|
+
--- Persistency table serializarion.
|
6
|
+
--- 2DO: If a better persistency dist with good readability becomes available change this code
|
7
|
+
-- This module contains functions that deal with serialization and loading of tables.
|
8
|
+
-- loadText - text 2 table
|
9
|
+
-- load - file 2 table
|
10
|
+
-- saveText - table 2 text
|
11
|
+
-- save - table 2 file
|
12
|
+
-- saveManifest - variant of save for manifests
|
13
|
+
|
14
|
+
module ("dist.persist", package.seeall)
|
15
|
+
|
16
|
+
local sys = require "dist.sys"
|
17
|
+
|
18
|
+
--- Serialize a table into text.
|
19
|
+
-- @param tbl table: Table to serialize.
|
20
|
+
-- @param d number: Intendation lenght.
|
21
|
+
-- @return out string: Serialized text.
|
22
|
+
local function serialize(o, d)
|
23
|
+
local out = ""
|
24
|
+
if not d then d = 0 end
|
25
|
+
|
26
|
+
if type(o) == "number" then
|
27
|
+
out = out .. tostring(o)
|
28
|
+
elseif type(o) == "table" then
|
29
|
+
out = out .."{\n"
|
30
|
+
for k,v in pairs(o) do
|
31
|
+
for f = 1,d do out = out .."\t" end
|
32
|
+
|
33
|
+
if type(k) ~="number" then
|
34
|
+
out = out .."\t['" ..tostring(k) .."'] = "
|
35
|
+
end
|
36
|
+
|
37
|
+
for f = 1,d do out = out .."\t" end
|
38
|
+
out = out .. serialize(v, d + 1)
|
39
|
+
if type(v) ~= "table" then out = out ..",\n" end
|
40
|
+
end
|
41
|
+
for f = 1,d do out = out .."\t" end
|
42
|
+
out = out .."},\n"
|
43
|
+
else
|
44
|
+
out = out .. '[[' .. tostring(o) .. ']]'
|
45
|
+
end
|
46
|
+
return out
|
47
|
+
end
|
48
|
+
|
49
|
+
--- Load table from text.
|
50
|
+
-- @param text string: Text to load table from.
|
51
|
+
-- @return table, log: Returns table on success, nil on failure and log message.
|
52
|
+
function loadText(text)
|
53
|
+
assert(type(text) == "string", "persist.loadText: Argument 'text' is not a string.")
|
54
|
+
|
55
|
+
local chunk, err = loadstring(text)
|
56
|
+
if not chunk then return false, "Failed to parse text " .. err end
|
57
|
+
|
58
|
+
local result = {}
|
59
|
+
|
60
|
+
setfenv(chunk, result)
|
61
|
+
local ok, ret, err = pcall(chunk)
|
62
|
+
if not ok then return false, ret end
|
63
|
+
return ret or result, "Sucessfully loaded table from text"
|
64
|
+
end
|
65
|
+
|
66
|
+
--- Load table from file.
|
67
|
+
-- @param filename string: File to load table from
|
68
|
+
-- @return table, log: Returns table on success, nil on failure and log message.
|
69
|
+
function load(filename)
|
70
|
+
assert(type(filename) == "string", "persist.load: Argument 'filename' is not a string.")
|
71
|
+
|
72
|
+
local chunk, err = loadfile(filename)
|
73
|
+
if not chunk then return false, "Cannot load from file " .. filename end
|
74
|
+
|
75
|
+
local result = {}
|
76
|
+
|
77
|
+
setfenv(chunk, result)
|
78
|
+
local ok, ret, err = pcall(chunk)
|
79
|
+
if not ok then return false, ret end
|
80
|
+
return ret or result, "Sucessfully loaded table from file " .. filename
|
81
|
+
end
|
82
|
+
|
83
|
+
--- Save table to string. Used for dist.info.
|
84
|
+
-- @param tbl table: Table to save.
|
85
|
+
-- @return out string: Serialized text.
|
86
|
+
function saveText(tbl)
|
87
|
+
assert(type(tbl) == "table", "persist.save: Argument 'tbl' is not a table.")
|
88
|
+
|
89
|
+
local out = ""
|
90
|
+
for k, v in pairs(tbl) do
|
91
|
+
-- Small fix for non alphanumeric strings (i know it looks ugly)
|
92
|
+
if not k:match("[^%w_]*$") then k = "_G['" .. k .. "']" end
|
93
|
+
if type(v) == 'table' then
|
94
|
+
-- little trick so top-level table won't have commas, but sub-tables will have
|
95
|
+
out = out .. k .. " = " .. serialize(v):gsub(',\n$', '\n') .."\n"
|
96
|
+
else
|
97
|
+
out = out .. k ..' = "' .. tostring(v):gsub('"','\\"') ..'"\n'
|
98
|
+
end
|
99
|
+
end
|
100
|
+
return out
|
101
|
+
end
|
102
|
+
|
103
|
+
--- Special serialization formating for manifests.
|
104
|
+
-- @param filename string: Path to save manifest to
|
105
|
+
-- @return ok, log: Returns true on success, nil on failure and log message.
|
106
|
+
function saveManifest(filename, dists)
|
107
|
+
assert(type(filename) == "string", "persist.saveManifest: Argument 'filename' is not a string.")
|
108
|
+
assert(type(dists) == "table", "persist.saveManifest: Argument 'dists' is not a table.")
|
109
|
+
|
110
|
+
local out = io.open(filename, "w")
|
111
|
+
if not out then return false, "Cannot write to file " .. filename end
|
112
|
+
|
113
|
+
out:write("return ");
|
114
|
+
out:write(serialize(dists).."true")
|
115
|
+
out:close()
|
116
|
+
return true, "Successfully saved manifest to " .. filename
|
117
|
+
end
|
118
|
+
|
119
|
+
--- Save table to file.
|
120
|
+
-- @param filename string: File to save table to.
|
121
|
+
-- @param tbl table: Table to save.
|
122
|
+
-- @return ok, log: Returns true on success, nil on failure and log message.
|
123
|
+
function save(filename, tbl)
|
124
|
+
assert(type(filename) == "string", "persist.save: Argument 'filename' is not a string.")
|
125
|
+
assert(type(tbl) == "table", "persist.save: Argument 'tbl' is not a table.")
|
126
|
+
|
127
|
+
local out = io.open(filename, "w")
|
128
|
+
if not out then return false, "Cannot write to file" .. filename end
|
129
|
+
out:write(saveText(tbl) or "")
|
130
|
+
out:close()
|
131
|
+
return true, "Successfully saved table to " .. filename
|
132
|
+
end
|
@@ -0,0 +1,436 @@
|
|
1
|
+
--- LuaDist UNIX and Windows system functions
|
2
|
+
-- Peter Drahoš, LuaDist Project, 2010
|
3
|
+
-- Original Code contributed to and then borrowed back from the LuaRocks Project
|
4
|
+
|
5
|
+
--- Host system dependent commands.
|
6
|
+
-- Override the default UNIX commands if needed for your platform.
|
7
|
+
-- Commands currently depend on popen and unzip being available. For future releases we would like
|
8
|
+
-- to use lua packages handling the compression issues. Additionally some filesystem functionality
|
9
|
+
-- not available in luafilesystem is emulated here.
|
10
|
+
|
11
|
+
module ("dist.sys", package.seeall)
|
12
|
+
|
13
|
+
local log = require "dist.log"
|
14
|
+
local config = require "dist.config"
|
15
|
+
local lfs = require "lfs"
|
16
|
+
|
17
|
+
--- Quote argument for shell processing.
|
18
|
+
-- Adds single quotes and escapes.
|
19
|
+
-- @param arg string: Unquoted argument.
|
20
|
+
-- @return string: Quoted argument.
|
21
|
+
function Q(arg)
|
22
|
+
assert(type(arg) == "string", "Q argument 'arg' is not a string.")
|
23
|
+
|
24
|
+
return "'" .. arg:gsub("\\", "\\\\"):gsub("'", "'\\''") .. "'"
|
25
|
+
end
|
26
|
+
|
27
|
+
--- Compose full system path or part of url
|
28
|
+
-- @args string: Path components
|
29
|
+
-- @return string: Path string
|
30
|
+
function path(...)
|
31
|
+
local args = ...
|
32
|
+
--if not args then return curDir() end
|
33
|
+
if type(...) == "string" then args = { ... } end
|
34
|
+
if type(args) ~= "table" then return nil end
|
35
|
+
|
36
|
+
if args[1]:match("^[%a:]*[/\\]") then
|
37
|
+
return table.concat(args,"/")
|
38
|
+
end
|
39
|
+
return curDir() .. "/" .. table.concat(args,"/")
|
40
|
+
end
|
41
|
+
|
42
|
+
--- Run the given system command.
|
43
|
+
-- The command is executed in the current directory in the dir stack.
|
44
|
+
-- @param cmd string: No quoting/escaping is applied to the command.
|
45
|
+
-- @return boolean: True if command succeeds, false otherwise.
|
46
|
+
function executeString(cmd)
|
47
|
+
assert(type(cmd) == "string", "sys.executeString: Argument 'cmd' is not a string.")
|
48
|
+
|
49
|
+
-- Command log
|
50
|
+
local execPath = path(config.temp, "execute.log")
|
51
|
+
|
52
|
+
-- Hande std output
|
53
|
+
local run = cmd
|
54
|
+
if not ( config.verbose or config.debug) then
|
55
|
+
run = run .. " >" .. execPath .. " 2>&1"
|
56
|
+
end
|
57
|
+
|
58
|
+
-- Run the command
|
59
|
+
local ok = os.execute( run )
|
60
|
+
|
61
|
+
-- Append the execution log to the log
|
62
|
+
local execFile, err = io.open( execPath, "r")
|
63
|
+
if execFile then
|
64
|
+
assert(execFile, "sys.executeString: Cannot create log.")
|
65
|
+
log.write("Command: " .. cmd .. "\n" .. execFile:read("*all") .. "\n")
|
66
|
+
execFile:close()
|
67
|
+
if not config.debug then os.remove(execPath) end
|
68
|
+
end
|
69
|
+
|
70
|
+
if ok~=0 then return nil, "Failed running command: " .. cmd end
|
71
|
+
return true, "Sucessfully executed command: " .. cmd
|
72
|
+
end
|
73
|
+
|
74
|
+
--- Run the given system command, quoting its arguments.
|
75
|
+
-- The command is executed in the current directory in the dir stack.
|
76
|
+
-- @param command string: The command to be executed. No quoting/escaping need to be applied.
|
77
|
+
-- @param ... strings: containing additional arguments, which are quoted.
|
78
|
+
-- @return ok, log: true on success, false on failure and log message.
|
79
|
+
function execute(command, ...)
|
80
|
+
assert(type(command) == "string", "execute argument 'command' is not a string.")
|
81
|
+
|
82
|
+
for k, arg in ipairs({...}) do
|
83
|
+
assert(type(arg) == "string", "execute argument #" .. tostring(k+1) .. "is not a string.")
|
84
|
+
command = command .. " " .. Q(arg)
|
85
|
+
end
|
86
|
+
return executeString(command)
|
87
|
+
end
|
88
|
+
|
89
|
+
--- Create a directory.
|
90
|
+
-- @param dir string: Path to create.
|
91
|
+
-- @return ok, log: true on success, false on failure and log message.
|
92
|
+
-- Succeeds if already exists.
|
93
|
+
function makeDir(dir)
|
94
|
+
assert(type(dir)=="string", "sys.makeDir: Argument 'dir' is not a string.")
|
95
|
+
dir = path(dir)
|
96
|
+
|
97
|
+
if isDir(dir) then return true end -- done
|
98
|
+
|
99
|
+
-- Find out if the base dir exists, if not make it
|
100
|
+
local base = dir:gsub("/[^/]*$","")
|
101
|
+
if base == "" then base = "/" end
|
102
|
+
-- Recursion!
|
103
|
+
if not isDir(base) then makeDir(base) end
|
104
|
+
|
105
|
+
-- Make directory
|
106
|
+
local ok = lfs.mkdir(dir)
|
107
|
+
if not ok then return nil, "Cannot create directory: " .. dir end
|
108
|
+
return true, "Created directory: " .. dir
|
109
|
+
end
|
110
|
+
|
111
|
+
--- Force delete a file, even if it is open.
|
112
|
+
-- If the file can't be deleted because it is currently open, this function
|
113
|
+
-- will try to rename the file to a temporary file in the same directory.
|
114
|
+
-- Windows in particular doesn't allow running executables to be deleted, but
|
115
|
+
-- it does allow them to be renamed. Cygwin 1.7 (unlike 1.5) does allow such
|
116
|
+
-- deletion but internally implements it via a mechanism like this. For
|
117
|
+
-- futher details, see
|
118
|
+
-- http://sourceforge.net/mailarchive/message.php?msg_name=bc4ed2190909261323i7c6280bfp6e7be6f70c713b0c%40mail.gmail.com
|
119
|
+
--
|
120
|
+
-- @param src - file name
|
121
|
+
function forceDelete(src)
|
122
|
+
os.remove(src) -- note: ignore fail
|
123
|
+
|
124
|
+
if exists(src) then -- still exists, try move instead
|
125
|
+
local tempfile
|
126
|
+
local MAX_TRIES = 10
|
127
|
+
for i=1,MAX_TRIES do
|
128
|
+
local test = src .. ".luadist-temporary-" .. i
|
129
|
+
os.remove(test) -- note: ignore fail
|
130
|
+
if not exists(test) then
|
131
|
+
tempfile = test
|
132
|
+
break
|
133
|
+
end
|
134
|
+
end
|
135
|
+
if not tempfile then
|
136
|
+
return nil, "Failed removing temporary files: " .. tempfile .. "*"
|
137
|
+
end
|
138
|
+
local ok, err = os.rename(src, tempfile)
|
139
|
+
if not ok then
|
140
|
+
return nil, "Failed renaming file: " .. err
|
141
|
+
end
|
142
|
+
end
|
143
|
+
return true
|
144
|
+
end
|
145
|
+
|
146
|
+
--- Move a file from one location to another.
|
147
|
+
-- @param src string: Pathname of source.
|
148
|
+
-- @param dest string: Pathname of destination.
|
149
|
+
-- @return ok, log: true on success, false on failure and log message.
|
150
|
+
function move(src, dest)
|
151
|
+
assert(type(src)=="string", "sys.move: Argument 'src' is not a string.")
|
152
|
+
assert(type(dest)=="string", "sys.move: Argument 'dest' is not a string.")
|
153
|
+
|
154
|
+
local ok, err = execute("mv -f", src, dest)
|
155
|
+
if not ok then return nil, "Failed moving source: " .. src .. " to: " .. dest .. " error: " .. err end
|
156
|
+
return true, "Moved source: " .. src .. " to: " .. dest
|
157
|
+
end
|
158
|
+
|
159
|
+
--- Recursive copy a file or directory.
|
160
|
+
-- @param src string: Pathname of source
|
161
|
+
-- @param dest string: Pathname of destination.
|
162
|
+
-- @param copy_contents boolean: if true, copies contents of
|
163
|
+
-- directory src into directory dest, creating dest
|
164
|
+
-- and parents of dest if they don't exist. (false if omitted)
|
165
|
+
-- @return ok, log: true on success, false on failure and log message.
|
166
|
+
function copy(src, dest, copy_contents)
|
167
|
+
assert(type(src)=="string", "copy argument 'src' is not a string.")
|
168
|
+
assert(type(dest)=="string", "copy argument 'dest' is not a string.")
|
169
|
+
|
170
|
+
if copy_contents and not isDir(dest) then
|
171
|
+
local ok, err = makeDir(dest)
|
172
|
+
if not ok then return nil, err end
|
173
|
+
end
|
174
|
+
|
175
|
+
local ok, err
|
176
|
+
if copy_contents then
|
177
|
+
ok, err = execute("cp -R -f -H " .. Q(src) .. [[/*]], dest)
|
178
|
+
else
|
179
|
+
ok, err = execute("cp -R -f -H ", src, dest)
|
180
|
+
end
|
181
|
+
if not ok then return nil, "Failed copying " .. src .. " to " .. dest .. ".\n" .. err end
|
182
|
+
return true
|
183
|
+
end
|
184
|
+
|
185
|
+
--- little helper function to get file depth (for directories its +1)
|
186
|
+
local function pathLen(dir)
|
187
|
+
local _, len = dir:gsub("[^\\/]+","")
|
188
|
+
return len - 1
|
189
|
+
end
|
190
|
+
|
191
|
+
--- Delete a file or a directory and all its contents.
|
192
|
+
-- For safety, this only accepts absolute paths.
|
193
|
+
-- @param dir string: Pathname of the file or directory to delete
|
194
|
+
-- @return ok, log: true on success, false on failure and log message. Returns success if already deleted.
|
195
|
+
function delete(dir)
|
196
|
+
assert(type(dir)=="string" and dir:match("^[%a:]*[/\\]"), "delete argument 'dir' is not a string or a full path.")
|
197
|
+
if not exists(dir) then return true end
|
198
|
+
return executeString("rm -rf " .. Q(dir))
|
199
|
+
end
|
200
|
+
|
201
|
+
--- List the contents of a directory.
|
202
|
+
-- @param path string: directory to list if not specified the current directory will be used.
|
203
|
+
-- @return table: an array of strings with the filenames representing the contents of a directory.
|
204
|
+
function dir(dir)
|
205
|
+
assert(not dir or type(dir) == "string", "dir argument 'dir' is not a string.")
|
206
|
+
dir = dir or curDir()
|
207
|
+
if not isDir(dir) then return nil, "Could not find directory " .. dir .. "." end
|
208
|
+
local files = {}
|
209
|
+
for file in lfs.dir(dir) do
|
210
|
+
if not file:match("^%.") then table.insert(files, file) end
|
211
|
+
end
|
212
|
+
return files
|
213
|
+
end
|
214
|
+
|
215
|
+
--- Get current directory.
|
216
|
+
-- @return string: current direcotry.
|
217
|
+
function curDir()
|
218
|
+
local dir, err = lfs.currentdir()
|
219
|
+
if not dir then return nil, err end
|
220
|
+
return dir:gsub("\\","/") -- Everyone loves win32
|
221
|
+
end
|
222
|
+
|
223
|
+
--- Test for existance of a file.
|
224
|
+
-- @param path string: filename to test
|
225
|
+
-- @return ok, log: true on success, false on failure and log message.
|
226
|
+
function exists(dir)
|
227
|
+
assert(type(dir)=="string", "exists argument 'dir' is not a string.")
|
228
|
+
return lfs.attributes(dir)
|
229
|
+
end
|
230
|
+
|
231
|
+
--- Test is pathname is a directory.
|
232
|
+
-- @param path string: pathname to test
|
233
|
+
-- @return ok, log: true on success, false on failure and log message.
|
234
|
+
function isDir(dir)
|
235
|
+
assert(type(dir)=="string", "isDir argument 'dir' is not a string.")
|
236
|
+
local attr, err = lfs.attributes(dir)
|
237
|
+
if not attr then return nil, "Failed to obtain attributes for " .. dir .. "." end
|
238
|
+
return attr.mode == "directory"
|
239
|
+
end
|
240
|
+
|
241
|
+
--- Test is pathname is a file.
|
242
|
+
-- @param path string: pathname to test
|
243
|
+
-- @return ok, log: true on success, false on failure and log message.
|
244
|
+
function isFile(dir)
|
245
|
+
assert(type(dir)=="string", "isFile argument 'dir' is not a string.")
|
246
|
+
local attr, err = lfs.attributes(dir)
|
247
|
+
if not attr then return nil, "Failed to obtain attributes for " .. dir .. "." end
|
248
|
+
return attr.mode == "file"
|
249
|
+
end
|
250
|
+
|
251
|
+
--- Recursively list the contents of a directory.
|
252
|
+
-- @param path string: directory to list if not specified the current directory will be used
|
253
|
+
-- @return table: an array of strings representing the contents of the directory structure
|
254
|
+
function list(dir)
|
255
|
+
assert(type(dir)=="string", "list argument 'path' is not a string.")
|
256
|
+
if not isDir(dir) then return nil, "Directory " .. dir .. " does not exist." end
|
257
|
+
|
258
|
+
local files = {}
|
259
|
+
|
260
|
+
local function collect (subdir)
|
261
|
+
subdir = subdir or ""
|
262
|
+
for file in lfs.dir(path(dir, subdir)) do
|
263
|
+
if not file:match("^%.") then
|
264
|
+
table.insert(files, subdir .. file)
|
265
|
+
if isDir(path(dir, subdir .. file)) then collect(subdir .. "/" .. file .. "/") end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
collect()
|
270
|
+
return files
|
271
|
+
end
|
272
|
+
|
273
|
+
--- Compress files in a .zip archive.
|
274
|
+
-- @param zipfile string: pathname of .zip archive to be created.
|
275
|
+
-- @param ... Filenames to be stored in the archive are given as additional arguments.
|
276
|
+
-- @return ok, log: true on success, false on failure and log message.
|
277
|
+
function zip(workdir, zipfile, ...)
|
278
|
+
assert (type(workdir)=="string", "zip argument 'workdir' is not a string.")
|
279
|
+
assert (type(zipfile)=="string", "zip argument 'zipfile' is not a string.")
|
280
|
+
return execute("cd " .. Q(workdir) .. " && zip -r", zipfile, ...)
|
281
|
+
end
|
282
|
+
|
283
|
+
--- Unpack an archive.
|
284
|
+
-- Extract the contents of an archive, detecting its format by filename extension.
|
285
|
+
-- @param archive string: Filename of archive.
|
286
|
+
-- @return ok, log: true on success, false on failure and log message.
|
287
|
+
function unzip(archive, dest)
|
288
|
+
assert(type(archive) == "string", "unpack argument 'archive' is not a string.")
|
289
|
+
assert(type(dest) == "string", "unpack agrument 'dest' is not a string.")
|
290
|
+
local ok
|
291
|
+
if archive:match("%.zip$") or archive:match("%.dist$") then
|
292
|
+
ok = executeString("unzip " .. Q(archive) .. " -d " .. Q(dest))
|
293
|
+
end
|
294
|
+
if not ok then
|
295
|
+
return false, "Failed extracting."
|
296
|
+
end
|
297
|
+
return dest
|
298
|
+
end
|
299
|
+
|
300
|
+
--- Extract file contents of a file from archive
|
301
|
+
-- @param zipfile string: pathname of .zip/.dist archive to read from.
|
302
|
+
-- @param file string: file to get contents of.
|
303
|
+
-- @return contents, err: returns contents of file or false and error message.
|
304
|
+
-- Requires io.popen (2DO: work when io.popen not available?)
|
305
|
+
function getZipFile(zipfile, file)
|
306
|
+
assert(type(zipfile) == "string", "unpack argument 'zipfile' is not a string.")
|
307
|
+
assert(type(file) == "string", "unpack agrument 'file' is not a string.")
|
308
|
+
|
309
|
+
-- Try to get contents
|
310
|
+
local f, err = io.popen("unzip -cp " .. Q(zipfile) .. " " .. Q(file))
|
311
|
+
if not f then return false, "Failed to extract " .. file .. " from " .. zipfile end
|
312
|
+
|
313
|
+
-- Read result
|
314
|
+
local content = f:read("*a")
|
315
|
+
f:close()
|
316
|
+
if content == "" then return false, "Failed to extract " .. file .. " from " .. zipfile end
|
317
|
+
return content
|
318
|
+
end
|
319
|
+
|
320
|
+
--- Override different functions for Windows
|
321
|
+
if config.arch == "Windows" then
|
322
|
+
|
323
|
+
--- Quote argument for shell processing (Windows).
|
324
|
+
-- Adds single quotes and escapes.
|
325
|
+
-- @param arg string: Unquoted argument.
|
326
|
+
-- @return string: Quoted argument.
|
327
|
+
function Q(arg)
|
328
|
+
assert(type(arg) == "string", "Q argument 'arg' is not a string.")
|
329
|
+
-- Quote DIR for Windows
|
330
|
+
if arg:match("^[\.a-zA-Z]?:?[\\/]") then
|
331
|
+
return '"' .. arg:gsub("//","\\"):gsub("/", "\\"):gsub('"', '\\"') .. '"'
|
332
|
+
end
|
333
|
+
-- URLs and anything else
|
334
|
+
return '"' .. arg:gsub('"', '\\"') .. '"'
|
335
|
+
end
|
336
|
+
|
337
|
+
--- Move a file from one location to another (Windows).
|
338
|
+
-- @param src string: Pathname of source.
|
339
|
+
-- @param dest string: Pathname of destination.
|
340
|
+
-- @return ok, log: true on success, false on failure and log message.
|
341
|
+
function move(src, dest)
|
342
|
+
assert(type(src)=="string", "sys.move: Argument 'src' is not a string.")
|
343
|
+
assert(type(dest)=="string", "sys.move: Argument 'dest' is not a string.")
|
344
|
+
|
345
|
+
-- note: copy+delete may be more reliable than move (e.g. copy across drives).
|
346
|
+
-- [improve: cleanup on failure?]
|
347
|
+
local ok, err = copy(src, dest)
|
348
|
+
if ok then
|
349
|
+
ok, err = delete(src)
|
350
|
+
end
|
351
|
+
if not ok then return nil, "Failed moving source: " .. src .. " to: " .. dest .. " error: " .. err end
|
352
|
+
return true, "Moved source: " .. src .. " to: " .. dest
|
353
|
+
end
|
354
|
+
|
355
|
+
--- Recursive copy a file or directory (Windows).
|
356
|
+
-- @param src string: Pathname of source
|
357
|
+
-- @param dest string: Pathname of destination.
|
358
|
+
-- If `src` is a directory, copies contents of `src` into contents of
|
359
|
+
-- directory `dest`. Otherwise, both must represent files.
|
360
|
+
-- @return ok, log: true on success, false on failure and log message.
|
361
|
+
function copy(src, dest)
|
362
|
+
assert(type(src)=="string", "copy argument 'src' is not a string.")
|
363
|
+
assert(type(dest)=="string", "copy argument 'dest' is not a string.")
|
364
|
+
|
365
|
+
-- Avoid ambiguous behavior: file <-> directory
|
366
|
+
--2DO - Improve?
|
367
|
+
if isFile(src) then
|
368
|
+
if isDir(dest) then
|
369
|
+
return nil, "ambiguous request to copy file " .. src .. " to directory " .. dest
|
370
|
+
end
|
371
|
+
elseif isDir(src) then
|
372
|
+
if isFile(dest) then
|
373
|
+
return nil, "ambiguous request to copy directory " .. src .. " to file " .. dest
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
--2DO: The below will cause problems if src and dest are the same.
|
378
|
+
|
379
|
+
-- For any destination files that will be overwritten,
|
380
|
+
-- force delete them to avoid conflicts from open files
|
381
|
+
-- in subsequent copy.
|
382
|
+
if isDir(src) then
|
383
|
+
local srcfiles = list(src)
|
384
|
+
for i = 1, #srcfiles do
|
385
|
+
local destfile = path(dest, srcfiles[i])
|
386
|
+
if isFile(destfile) then
|
387
|
+
local ok, err = forceDelete(destfile)
|
388
|
+
if not ok then return nil, "Failed removing file: " .. destfile .. " " .. err end
|
389
|
+
end
|
390
|
+
end
|
391
|
+
elseif isFile(src) then
|
392
|
+
local ok, err = forceDelete(dest)
|
393
|
+
if not ok then return nil, "Failed removing file: " .. dest .. " " .. err end
|
394
|
+
end
|
395
|
+
|
396
|
+
local ok, err
|
397
|
+
if isDir(src) then
|
398
|
+
-- note: "xcopy /E /I /Y" copies contents of `src` into contents
|
399
|
+
-- of `dest` and creates all leading directories of `dest` if needed.
|
400
|
+
ok, err = execute("xcopy /I /E /Y " .. Q(src), dest)
|
401
|
+
else
|
402
|
+
ok, err = execute("copy /Y", src, dest)
|
403
|
+
end
|
404
|
+
if not ok then return nil, "Failed copying " .. src .. " to " .. dest .. ".\n" .. err end
|
405
|
+
-- note: The /Q flag on xcopy is not fully quiet; it prints
|
406
|
+
-- "1 File(s) copied". Copy lacks a /Q flag.
|
407
|
+
|
408
|
+
return true
|
409
|
+
end
|
410
|
+
|
411
|
+
--- Delete a file or a directory and all its contents (Windows).
|
412
|
+
-- For safety, this only accepts absolute paths.
|
413
|
+
-- @param dir string: Pathname of the file or directory to delete
|
414
|
+
-- @return ok, log: true on success, false on failure and log message.
|
415
|
+
-- Returns success if already deleted.
|
416
|
+
function delete(dir)
|
417
|
+
assert(type(dir)=="string" and dir:match("^[%a:]*[/\\]"), "delete argument 'dir' is not a string or a full path.")
|
418
|
+
-- Note: `del /S` recursively deletes files but not directories.
|
419
|
+
-- `rmdir /S` recursively deletes files and directories but does not
|
420
|
+
-- work if its parameter is a file.
|
421
|
+
if not exists(dir) then
|
422
|
+
return true
|
423
|
+
elseif isDir(dir) then
|
424
|
+
local ok, err = executeString("rmdir /S /Q " .. Q(dir))
|
425
|
+
if not ok then
|
426
|
+
return nil, "Could not recursively delete directory " .. dir .. " . " .. err
|
427
|
+
end
|
428
|
+
else
|
429
|
+
local ok, err = os.remove(dir)
|
430
|
+
if not ok then
|
431
|
+
return nil, "Could not delete file " .. dir .. " . " .. err
|
432
|
+
end
|
433
|
+
end
|
434
|
+
return true
|
435
|
+
end
|
436
|
+
end
|