Tamar 0.7.2 → 0.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|