I am tired of this timing out
[QuestHelper.git] / Development / fileutil.lua
blobddec601c656a3317e5afaba8a669bce9b8953ea4
1 FileUtil = {}
3 --[[ Note:
5 fileHash and forEachFile will probably need replacements for other operating systems. ]]
7 --[[ Warning:
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, "^~(.*)$"))
22 if home_path then
23 return (is_windows and (os.getenv("HOMEDRIVE")..os.getenv("HOMEPATH")) or os.getenv("HOME"))..home_path
24 end
26 return filename
27 end
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
37 local result = ""
38 for i=1,string.len(filename) do
39 local c = string.sub(filename, i, i)
40 if c == "/" then
41 c = "\\"
42 elseif string.find(c, "[^\\%.%a%d]") then
43 c = "^"..c
44 end
46 result = result .. c
47 end
48 return result
49 end
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
59 local result = ""
60 for i=1,string.len(filename) do
61 local c = string.sub(filename, i, i)
62 if c == "\\" then
63 c = "/"
64 elseif string.find(c, "[^/%.%-%a%d]") then
65 c = "\\"..c
66 end
68 result = result .. c
69 end
71 return result
72 end
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)
78 end
80 FileUtil.fileHash = function(filename)
81 local stream = io.popen(string.format("sha1sum %s", FileUtil.quoteFile(filename)))
83 if not stream then
84 print("Failed to calculate hash: "..filename)
85 return nil
86 end
88 local line = stream:read()
89 io.close(stream)
90 if line then
91 return select(3, string.find(line, string.format("^([abcdef%%d]+) %s$", escapeForPattern(filename))))
92 end
93 end
95 FileUtil.fileExists = function(filename)
96 local stream = io.open(FileUtil.fileName(filename), "r")
97 if stream then
98 local exists = stream:read() ~= nil
99 io.close(stream)
100 return exists
102 return false
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")
107 if stream then
108 local result = stream:read("*line")
109 io.close(stream)
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, "([^/\\]*)$")))
125 if extra > 0 then
126 assert(extra%2==0, "Odd number of arguments.")
127 local src = io.open(in_name, "rb")
128 if src then
129 local dest = io.open(out_name, "wb")
130 if dest then
131 while true do
132 local original = src:read("*line")
133 if not original then break end
134 local eol
135 original, eol = select(3, string.find(original, "^(.-)(\r?)$")) -- Try to keep the CR in CRLF codes intact.
136 local replacement = original
137 for i = 1,extra,2 do
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")
147 io.close(dest)
148 else
149 print("Failed to copy "..in_name.." to "..out_name.."; couldn't open "..out_name)
151 io.close(src)
152 else
153 print("Failed to copy "..in_name.." to "..out_name.."; couldn't open "..in_name)
155 else
156 local f = assert(io.open(in_name, "rb"))
157 local d = f:read("*all")
158 f:close()
159 f = assert(io.open(out_name, "wb"))
160 f:write(d)
161 f:close()
165 FileUtil.forEachFile = function(directory, func)
166 if directory == "" then
167 directory = "."
170 local stream = io.popen(string.format(is_windows and "DIR /B %s" or "ls -1 %s", FileUtil.quoteFile(directory)))
172 if not stream then
173 print("Failed to read directory contents: "..directory)
174 return
177 while true do
178 local filename = stream:read()
179 if not filename then break end
180 filename = directory.."/"..filename
182 if FileUtil.fileExists(filename) then
183 func(filename)
187 io.close(stream)
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))
193 assert(false)
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..")")
208 else
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)))
220 os.execute("pwd")
221 os.exit(1)
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))
251 os.execute("pwd")
252 print("lulz")
253 os.exit(1)
254 else
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)
261 os.exit(1)
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))
280 return rv == 0