Added machine translated descriptions in the TOC file.
[QuestHelper.git] / Development / fileutil.lua
blob69a5914fe8e8086c8b9c3990b5c42c2215c32c3d
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 -- TODO: Windows version of this.
107 local stream = io.popen(string.format("file -b %s", FileUtil.quoteFile(filename)), "r")
108 if stream then
109 local result = stream:read("*line")
110 io.close(stream)
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, "([^/\\]*)$")))
126 if extra > 0 then
127 assert(extra%2==0, "Odd number of arguments.")
128 local src = io.open(in_name, "rb")
129 if src then
130 local dest = io.open(out_name, "wb")
131 if dest then
132 while true do
133 local original = src:read("*line")
134 if not original then break end
135 local eol
136 original, eol = select(3, string.find(original, "^(.-)(\r?)$")) -- Try to keep the CR in CRLF codes intact.
137 local replacement = original
138 for i = 1,extra,2 do
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")
148 io.close(dest)
149 else
150 print("Failed to copy "..in_name.." to "..out_name)
152 io.close(src)
153 else
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
163 directory = "."
166 local stream = io.popen(string.format(is_windows and "DIR /B %s" or "ls -1 %s", FileUtil.quoteFile(directory)))
168 if not stream then
169 print("Failed to read directory contents: "..directory)
170 return
173 while true do
174 local filename = stream:read()
175 if not filename then break end
176 filename = directory.."/"..filename
178 if FileUtil.fileExists(filename) then
179 func(filename)
183 io.close(stream)
186 FileUtil.extension = function(filename)
187 local ext = select(3, string.find(filename, ".*%.(.-)$"))
188 if ext and not string.find(ext, "[/\\]") then
189 return ext
191 return ""
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..")")
200 else
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)