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 -- TODO: Windows version of this.
107 local stream
= io
.popen(string.format("file -b %s", FileUtil
.quoteFile(filename
)), "r")
109 local result
= stream
:read("*line")
111 return 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
)
154 print("Failed to copy "..in_name
.." to "..out_name
)
156 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
157 print("Failed to copy "..in_name
.." to "..out_name
)
161 FileUtil
.forEachFile
= function(directory
, func
)
162 if directory
== "" then
166 local stream
= io
.popen(string.format(is_windows
and "DIR /B %s" or "ls -1 %s", FileUtil
.quoteFile(directory
)))
169 print("Failed to read directory contents: "..directory
)
174 local filename
= stream
:read()
175 if not filename
then break end
176 filename
= directory
.."/"..filename
178 if FileUtil
.fileExists(filename
) then
186 FileUtil
.extension
= function(filename
)
187 local ext
= select(3, string.find(filename
, ".*%.(.-)$"))
188 if ext
and not string.find(ext
, "[/\\]") then
194 FileUtil
.updateSVNRepo
= function(url
, directory
)
195 -- Check for the SVN entries file, which should exist regardless of OS; fileExists doesn't work for directories under Windows.
196 if FileUtil
.fileExists(directory
.."/.svn/entries") then
197 if os
.execute(string.format("svn up -q %s", FileUtil
.quoteFile(directory
))) ~= 0 then
198 print("Failed to update svn repository: "..directory
.." ("..url
..")")
201 -- quoteFile on Windows results in invalid URLs, so just wrap it in quotes and be done with it
202 if os
.execute(string.format("svn co -q %s %s", is_windows
and "\""..url
.."\"" or FileUtil
.quoteFile(url
), FileUtil
.quoteFile(directory
))) ~= 0 then
203 print("Failed to up fetch svn repository: "..directory
.." ("..url
..")")
208 FileUtil
.createDirectory
= function(directory
)
209 if os
.execute(string.format(is_windows
and "MD %s" or "mkdir -p %s", FileUtil
.quoteFile(directory
))) ~= 0 then
210 print("Failed to create directory: "..directory
)
214 FileUtil
.unlinkDirectory
= function(directory
)
215 if os
.execute(string.format(is_windows
and "RMDIR /S /Q %s" or "rm -rf %s", FileUtil
.quoteFile(directory
))) ~= 0 then
216 print("Failed to unlink directory: "..directory
)
220 FileUtil
.unlinkFile
= function(filename
)
221 if os
.execute(string.format(is_windows
and "DEL /Q %s" or "rm -rf %s", FileUtil
.quoteFile(filename
))) ~= 0 then
222 print("Failed to unlink file: "..filename
)
226 FileUtil
.convertImage
= function(source
, dest
)
227 if os
.execute(string.format("convert -background None %s %s", FileUtil
.quoteFile(source
), FileUtil
.quoteFile(dest
))) ~= 0 then
228 print("Failed to convert: "..source
)
232 FileUtil
.createZipArchive
= function(directory
, archive
)
233 if os
.execute(string.format("zip -rq9 %s %s", FileUtil
.quoteFile(archive
), FileUtil
.quoteFile(directory
))) ~= 0 then
234 print("Failed to create zip archive: "..archive
)
238 FileUtil
.create7zArchive
= function(directory
, archive
)
239 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
240 print("Failed to create 7z archive: "..archive
)