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
)
155 elseif os
.execute(string.format(is_windows
and "COPY %s %s" or "cp %s %s", FileUtil
.quoteFile(in_name
), FileUtil
.quoteFile(out_name
))) ~= 0 then
156 print("Failed to copy "..in_name
.." to "..out_name
)
160 FileUtil
.forEachFile
= function(directory
, func
)
161 if directory
== "" then
165 local stream
= io
.popen(string.format(is_windows
and "DIR /B %s" or "ls -1 %s", FileUtil
.quoteFile(directory
)))
168 print("Failed to read directory contents: "..directory
)
173 local filename
= stream
:read()
174 if not filename
then break end
175 filename
= directory
.."/"..filename
177 if FileUtil
.fileExists(filename
) then
185 FileUtil
.extension
= function(filename
)
186 local ext
= select(3, string.find(filename
, "%.([^%s/\\]-)$"))
187 return ext
and string.lower(ext
) or ""
190 FileUtil
.updateSVNRepo
= function(url
, directory
)
191 -- Check for the SVN entries file, which should exist regardless of OS; fileExists doesn't work for directories under Windows.
192 if FileUtil
.fileExists(directory
.."/.svn/entries") then
193 if os
.execute(string.format("svn up -q %s", FileUtil
.quoteFile(directory
))) ~= 0 then
194 print("Failed to update svn repository: "..directory
.." ("..url
..")")
197 -- quoteFile on Windows results in invalid URLs, so just wrap it in quotes and be done with it
198 if os
.execute(string.format("svn co -q %s %s", is_windows
and "\""..url
.."\"" or FileUtil
.quoteFile(url
), FileUtil
.quoteFile(directory
))) ~= 0 then
199 print("Failed to up fetch svn repository: "..directory
.." ("..url
..")")
204 FileUtil
.createDirectory
= function(directory
)
205 if os
.execute(string.format(is_windows
and "MD %s" or "mkdir -p %s", FileUtil
.quoteFile(directory
))) ~= 0 then
206 print("Failed to create directory: "..directory
)
210 FileUtil
.unlinkDirectory
= function(directory
)
211 if os
.execute(string.format(is_windows
and "RMDIR /S /Q %s" or "rm -rf %s", FileUtil
.quoteFile(directory
))) ~= 0 then
212 print("Failed to unlink directory: "..directory
)
216 FileUtil
.unlinkFile
= function(filename
)
217 if os
.execute(string.format(is_windows
and "DEL /Q %s" or "rm -rf %s", FileUtil
.quoteFile(filename
))) ~= 0 then
218 print("Failed to unlink file: "..filename
)
222 FileUtil
.convertImage
= function(source
, dest
)
223 if source
~= dest
then
224 if FileUtil
.extension(source
) == "svg" then
225 -- Because convert doesn't properly render SVG files,
226 -- I'm going to instead use rsvg to render them to some temporary location,
227 -- and then use convert on the temporary file.
228 local temp
= os
.tmpname()..".png"
229 if os
.execute(string.format("rsvg -fpng %s %s", FileUtil
.quoteFile(source
), FileUtil
.quoteFile(temp
))) ~= 0 then
230 print("Failed to convert: "..source
)
232 FileUtil
.convertImage(temp
, dest
)
233 FileUtil
.unlinkFile(temp
)
235 elseif os
.execute(string.format("convert -background None %s %s", FileUtil
.quoteFile(source
), FileUtil
.quoteFile(dest
))) ~= 0 then
236 print("Failed to convert: "..source
)
241 FileUtil
.createZipArchive
= function(directory
, archive
)
242 if os
.execute(string.format("zip -rq9 %s %s", FileUtil
.quoteFile(archive
), FileUtil
.quoteFile(directory
))) ~= 0 then
243 print("Failed to create zip archive: "..archive
)
247 FileUtil
.create7zArchive
= function(directory
, archive
)
248 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
249 print("Failed to create 7z archive: "..archive
)