5 fileHash and forEachFile will probably need replacements for other operating systems. ]]
9 Pretty much all these functions can be made to do something malicious if given bad file names;
10 don't use input from untrusted sources. ]]
12 -- Our horrible test to check if you're using Windows or not.
13 local is_windows
= os
.getenv("HOMEDRIVE") ~= nil or
14 os
.getenv("WINDIR") ~= nil or
15 os
.getenv("OS") == "Windows_NT"
17 local home
= os
.getenv("HOME")
19 FileUtil
.fileName
= function(filename
)
20 local home_path
= select(3, string.find(filename
, "^~(.*)$"))
23 return (is_windows
and (os
.getenv("HOMEDRIVE")..os
.getenv("HOMEPATH")) or os
.getenv("HOME"))..home_path
30 FileUtil
.quoteFileWindows
= function (filename
)
31 -- Escapes filenames in Windows, and converts slashes to backslashes.
33 filename
= FileUtil
.fileName(filename
)
35 if filename
== "" then return "\"\"" end
38 for i
=1,string.len(filename
) do
39 local c
= string.sub(filename
, i
, i
)
42 elseif string.find(c
, "[^\\%.%a%d]") then
51 FileUtil
.quoteFileNix
= function (filename
)
52 -- Escapes filenames in *nix, and converts backslashes to slashes.
53 -- Also used directly for URLs, which are always *nix style paths
55 filename
= FileUtil
.fileName(filename
)
57 if filename
== "" then return "\"\"" end
60 for i
=1,string.len(filename
) do
61 local c
= string.sub(filename
, i
, i
)
64 elseif string.find(c
, "[^/%.%-%a%d]") then
74 FileUtil
.quoteFile
= is_windows
and FileUtil
.quoteFileWindows
or FileUtil
.quoteFileNix
76 local function escapeForPattern(text
)
77 return string.gsub(text
, "[%%%^%$%.%+%*%-%?%[%]]", function (x
) return "%"..x
end)
80 FileUtil
.fileHash
= function(filename
)
81 print(string.format("Hashing " .. filename
))
82 local stream
= io
.popen(string.format("sha1sum %s", FileUtil
.quoteFile(filename
)))
85 print("Failed to calculate hash: "..filename
)
89 local line
= stream
:read()
92 return select(3, string.find(line
, string.format("^([abcdef%%d]+) %s$", escapeForPattern(filename
))))
96 FileUtil
.fileExists
= function(filename
)
97 local stream
= io
.open(FileUtil
.fileName(filename
), "r")
99 local exists
= stream
:read() ~= nil
106 FileUtil
.isDirectory
= function(filename
)
107 local stream
= io
.popen(string.format(is_windows
and "DIR /B /AD %s" or "file -b %s", FileUtil
.quoteFile(filename
)), "r")
109 local result
= stream
:read("*line")
111 return is_windows
and (result
~= "File Not Found") or (result
== "directory")
113 error("Failed to execute 'file' command.")
116 -- Extra strings passed to copyFile are pattern/replacement pairs, applied to
117 -- each line of the file being copied.
118 FileUtil
.copyFile
= function(in_name
, out_name
, ...)
119 local extra
= select("#", ...)
121 if FileUtil
.isDirectory(out_name
) then
122 -- If out_name is a directory, change it to a filename.
123 out_name
= string.format("%s/%s", out_name
, select(3, string.find(in_name
, "([^/\\]*)$")))
127 assert(extra
%2==0, "Odd number of arguments.")
128 local src
= io
.open(in_name
, "rb")
130 local dest
= io
.open(out_name
, "wb")
133 local original
= src
:read("*line")
134 if not original
then break end
136 original
, eol
= select(3, string.find(original
, "^(.-)(\r?)$")) -- Try to keep the CR in CRLF codes intact.
137 local replacement
= original
139 local a
, b
= select(i
, ...)
140 replacement
= string.gsub(replacement
, a
, b
)
143 -- If we make a line blank, and it wasn't blank before, we omit the line.
144 if original
== replacement
or replacement
~= "" then
145 dest
:write(replacement
, eol
, "\n")
150 print("Failed to copy "..in_name
.." to "..out_name
.."; couldn't open "..out_name
)
154 print("Failed to copy "..in_name
.." to "..out_name
.."; couldn't open "..in_name
)
157 local f
= assert(io
.open(in_name
, "rb"))
158 local d
= f
:read("*all")
160 f
= assert(io
.open(out_name
, "wb"))
166 FileUtil
.forEachFile
= function(directory
, func
)
167 if directory
== "" then
171 local stream
= io
.popen(string.format(is_windows
and "DIR /B %s" or "ls -1 %s", FileUtil
.quoteFile(directory
)))
174 print("Failed to read directory contents: "..directory
)
179 local filename
= stream
:read()
180 if not filename
then break end
181 filename
= directory
.."/"..filename
183 if FileUtil
.fileExists(filename
) then
191 FileUtil
.copyDirectoryRecursively
= function(src
, dest
)
192 if os
.execute(string.format("cp -r %s %s", src
, dest
)) ~= 0 then
193 print(string.format("Failed to copy %s to %s", src
, dest
))
197 os
.execute(string.format("rm -rf %s/.*", dest
))
200 FileUtil
.extension
= function(filename
)
201 local ext
= select(3, string.find(filename
, "%.([^%s/\\]-)$"))
202 return ext
and string.lower(ext
) or ""
205 FileUtil
.updateSVNRepo
= function(url
, directory
)
206 -- Check for the SVN entries file, which should exist regardless of OS; fileExists doesn't work for directories under Windows.
207 if FileUtil
.fileExists(directory
.."/.svn/entries") then
208 if os
.execute(string.format("svn up -q %s", FileUtil
.quoteFile(directory
))) ~= 0 then
209 print("Failed to update svn repository: "..directory
.." ("..url
..")")
212 -- quoteFile on Windows results in invalid URLs, so just wrap it in quotes and be done with it
213 if os
.execute(string.format("svn co -q %s %s", is_windows
and "\""..url
.."\"" or FileUtil
.quoteFile(url
), FileUtil
.quoteFile(directory
))) ~= 0 then
214 print("Failed to up fetch svn repository: "..directory
.." ("..url
..")")
219 FileUtil
.createDirectory
= function(directory
)
220 if os
.execute(string.format(is_windows
and "MD %s" or "mkdir -p %s", FileUtil
.quoteFile(directory
))) ~= 0 then
221 print("Failed to create directory: "..directory
)
222 print(string.format(is_windows
and "MD %s" or "mkdir -p %s", FileUtil
.quoteFile(directory
)))
228 FileUtil
.unlinkDirectory
= function(directory
)
229 if os
.execute(string.format(is_windows
and "RMDIR /S /Q %s" or "rm -rf %s", FileUtil
.quoteFile(directory
))) ~= 0 then
230 print("Failed to unlink directory: "..directory
)
234 FileUtil
.unlinkFile
= function(file
)
235 if not os
.remove(file
) then
236 print("Couldn't remove file " .. file
)
240 FileUtil
.convertImage
= function(source
, dest
)
241 if source
~= dest
then
242 if FileUtil
.extension(source
) == "svg" then
243 -- Because convert doesn't properly render SVG files,
244 -- I'm going to instead use rsvg to render them to some temporary location,
245 -- and then use convert on the temporary file.
246 local temp
= os
.tmpname()..".png"
247 print(string.format("rsvg -fpng %s %s", FileUtil
.quoteFile(source
), FileUtil
.quoteFile(temp
)))
248 if os
.execute(string.format("rsvg -fpng %s %s", FileUtil
.quoteFile(source
), FileUtil
.quoteFile(temp
))) ~= 0 then
249 print("Failed to convert: "..source
)
250 print(tostring(os
.execute(string.format("rsvg -fpng %s %s", FileUtil
.quoteFile(source
), FileUtil
.quoteFile(temp
))) ~= 0))
251 print(tostring(os
.execute(string.format("rsvg -fpng %s %s", FileUtil
.quoteFile(source
), FileUtil
.quoteFile(temp
))) ~= 0))
252 print(tostring(os
.execute(string.format("rsvg -fpng Development/%s %s", FileUtil
.quoteFile(source
), FileUtil
.quoteFile(temp
))) ~= 0))
253 print(tostring(os
.execute(string.format("rsvg -fpng %s %s", FileUtil
.quoteFile(source
), FileUtil
.quoteFile(temp
))) ~= 0))
258 FileUtil
.convertImage(temp
, dest
)
259 FileUtil
.unlinkFile(temp
)
261 elseif os
.execute(string.format("convert -background None %s %s", FileUtil
.quoteFile(source
), FileUtil
.quoteFile(dest
))) ~= 0 then
262 print(string.format("convert -background None %s %s", FileUtil
.quoteFile(source
), FileUtil
.quoteFile(dest
)))
263 print("Failed to convert: "..source
)
269 FileUtil
.createZipArchive
= function(directory
, archive
)
270 if os
.execute(string.format("zip -rq9 %s %s", FileUtil
.quoteFile(archive
), FileUtil
.quoteFile(directory
))) ~= 0 then
271 print("Failed to create zip archive: "..archive
)
275 FileUtil
.create7zArchive
= function(directory
, archive
)
276 if os
.execute(string.format("7z a -t7z -m0=lzma -mx=9 -mfb=64 -md=32m -ms=on %s %s", FileUtil
.quoteFile(archive
), FileUtil
.quoteFile(directory
))) ~= 0 then
277 print("Failed to create 7z archive: "..archive
)
281 FileUtil
.fileContains
= function(filename
, text
)
282 local rv
= os
.execute(string.format("grep %s %s", text
, filename
))