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 local stream
= io
.popen(string.format("sha1sum %s", FileUtil
.quoteFile(filename
)))
84 print("Failed to calculate hash: "..filename
)
88 local line
= stream
:read()
91 return select(3, string.find(line
, string.format("^([abcdef%%d]+) %s$", escapeForPattern(filename
))))
95 FileUtil
.fileExists
= function(filename
)
96 local stream
= io
.open(FileUtil
.fileName(filename
), "r")
98 local exists
= stream
:read() ~= nil
105 FileUtil
.isDirectory
= function(filename
)
106 local stream
= io
.popen(string.format(is_windows
and "DIR /B /AD %s" or "file -b %s", FileUtil
.quoteFile(filename
)), "r")
108 local result
= stream
:read("*line")
110 return is_windows
and (result
~= "File Not Found") or (result
== "directory")
112 error("Failed to execute 'file' command.")
115 -- Extra strings passed to copyFile are pattern/replacement pairs, applied to
116 -- each line of the file being copied.
117 FileUtil
.copyFile
= function(in_name
, out_name
, ...)
118 local extra
= select("#", ...)
120 if FileUtil
.isDirectory(out_name
) then
121 -- If out_name is a directory, change it to a filename.
122 out_name
= string.format("%s/%s", out_name
, select(3, string.find(in_name
, "([^/\\]*)$")))
126 assert(extra
%2==0, "Odd number of arguments.")
127 local src
= io
.open(in_name
, "rb")
129 local dest
= io
.open(out_name
, "wb")
132 local original
= src
:read("*line")
133 if not original
then break end
135 original
, eol
= select(3, string.find(original
, "^(.-)(\r?)$")) -- Try to keep the CR in CRLF codes intact.
136 local replacement
= original
138 local a
, b
= select(i
, ...)
139 replacement
= string.gsub(replacement
, a
, b
)
142 -- If we make a line blank, and it wasn't blank before, we omit the line.
143 if original
== replacement
or replacement
~= "" then
144 dest
:write(replacement
, eol
, "\n")
149 print("Failed to copy "..in_name
.." to "..out_name
.."; couldn't open "..out_name
)
153 print("Failed to copy "..in_name
.." to "..out_name
.."; couldn't open "..in_name
)
156 local f
= assert(io
.open(in_name
, "rb"))
157 local d
= f
:read("*all")
159 f
= assert(io
.open(out_name
, "wb"))
165 FileUtil
.forEachFile
= function(directory
, func
)
166 if directory
== "" then
170 local stream
= io
.popen(string.format(is_windows
and "DIR /B %s" or "ls -1 %s", FileUtil
.quoteFile(directory
)))
173 print("Failed to read directory contents: "..directory
)
178 local filename
= stream
:read()
179 if not filename
then break end
180 filename
= directory
.."/"..filename
182 if FileUtil
.fileExists(filename
) then
190 FileUtil
.copyDirectoryRecursively
= function(src
, dest
)
191 if os
.execute(string.format("cp -r %s %s", src
, dest
)) ~= 0 then
192 print(string.format("Failed to copy %s to %s", src
, dest
))
197 FileUtil
.extension
= function(filename
)
198 local ext
= select(3, string.find(filename
, "%.([^%s/\\]-)$"))
199 return ext
and string.lower(ext
) or ""
202 FileUtil
.updateSVNRepo
= function(url
, directory
)
203 -- Check for the SVN entries file, which should exist regardless of OS; fileExists doesn't work for directories under Windows.
204 if FileUtil
.fileExists(directory
.."/.svn/entries") then
205 if os
.execute(string.format("svn up -q %s", FileUtil
.quoteFile(directory
))) ~= 0 then
206 print("Failed to update svn repository: "..directory
.." ("..url
..")")
209 -- quoteFile on Windows results in invalid URLs, so just wrap it in quotes and be done with it
210 if os
.execute(string.format("svn co -q %s %s", is_windows
and "\""..url
.."\"" or FileUtil
.quoteFile(url
), FileUtil
.quoteFile(directory
))) ~= 0 then
211 print("Failed to up fetch svn repository: "..directory
.." ("..url
..")")
216 FileUtil
.createDirectory
= function(directory
)
217 if os
.execute(string.format(is_windows
and "MD %s" or "mkdir -p %s", FileUtil
.quoteFile(directory
))) ~= 0 then
218 print("Failed to create directory: "..directory
)
219 print(string.format(is_windows
and "MD %s" or "mkdir -p %s", FileUtil
.quoteFile(directory
)))
225 FileUtil
.unlinkDirectory
= function(directory
)
226 if os
.execute(string.format(is_windows
and "RMDIR /S /Q %s" or "rm -rf %s", FileUtil
.quoteFile(directory
))) ~= 0 then
227 print("Failed to unlink directory: "..directory
)
231 FileUtil
.unlinkFile
= function(file
)
232 if not os
.remove(file
) then
233 print("Couldn't remove file " .. file
)
237 FileUtil
.convertImage
= function(source
, dest
)
238 if source
~= dest
then
239 if FileUtil
.extension(source
) == "svg" then
240 -- Because convert doesn't properly render SVG files,
241 -- I'm going to instead use rsvg to render them to some temporary location,
242 -- and then use convert on the temporary file.
243 local temp
= os
.tmpname()..".png"
244 print(string.format("rsvg -fpng %s %s", FileUtil
.quoteFile(source
), FileUtil
.quoteFile(temp
)))
245 if os
.execute(string.format("rsvg -fpng %s %s", FileUtil
.quoteFile(source
), FileUtil
.quoteFile(temp
))) ~= 0 then
246 print("Failed to convert: "..source
)
247 print(tostring(os
.execute(string.format("rsvg -fpng %s %s", FileUtil
.quoteFile(source
), FileUtil
.quoteFile(temp
))) ~= 0))
248 print(tostring(os
.execute(string.format("rsvg -fpng %s %s", FileUtil
.quoteFile(source
), FileUtil
.quoteFile(temp
))) ~= 0))
249 print(tostring(os
.execute(string.format("rsvg -fpng Development/%s %s", FileUtil
.quoteFile(source
), FileUtil
.quoteFile(temp
))) ~= 0))
250 print(tostring(os
.execute(string.format("rsvg -fpng %s %s", FileUtil
.quoteFile(source
), FileUtil
.quoteFile(temp
))) ~= 0))
255 FileUtil
.convertImage(temp
, dest
)
256 FileUtil
.unlinkFile(temp
)
258 elseif os
.execute(string.format("convert -background None %s %s", FileUtil
.quoteFile(source
), FileUtil
.quoteFile(dest
))) ~= 0 then
259 print(string.format("convert -background None %s %s", FileUtil
.quoteFile(source
), FileUtil
.quoteFile(dest
)))
260 print("Failed to convert: "..source
)
266 FileUtil
.createZipArchive
= function(directory
, archive
)
267 if os
.execute(string.format("zip -rq9 %s %s", FileUtil
.quoteFile(archive
), FileUtil
.quoteFile(directory
))) ~= 0 then
268 print("Failed to create zip archive: "..archive
)
272 FileUtil
.create7zArchive
= function(directory
, archive
)
273 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
274 print("Failed to create 7z archive: "..archive
)
278 FileUtil
.fileContains
= function(filename
, text
)
279 local rv
= os
.execute(string.format("grep %s %s", text
, filename
))