FileSystem.lua 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. --[[
  2. MIT License
  3. Copyright (c) 2019 Mitchell Davis <coding.jackalope@gmail.com>
  4. Permission is hereby granted, free of charge, to any person obtaining a copy
  5. of this software and associated documentation files (the "Software"), to deal
  6. in the Software without restriction, including without limitation the rights
  7. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. copies of the Software, and to permit persons to whom the Software is
  9. furnished to do so, subject to the following conditions:
  10. The above copyright notice and this permission notice shall be included in all
  11. copies or substantial portions of the Software.
  12. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  13. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  14. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  15. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  16. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  17. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  18. SOFTWARE.
  19. --]]
  20. local FileSystem = {}
  21. function FileSystem.Separator()
  22. -- Lua/Love2D returns all paths with back slashes.
  23. return "/"
  24. end
  25. local OS = love.system.getOS() -- Cache it, as it's not expected to change
  26. local function BackslashEscapeQuotes(Str)
  27. return "'" .. ("\\'"):rep(#Str) .. "'"
  28. end
  29. local function EscParam(Param)
  30. if OS == "Windows" then
  31. -- TODO: safer parameter quoting
  32. return '"' .. Param .. '"'
  33. end
  34. return "'" .. Param:gsub("'+", BackslashEscapeQuotes) .. "'"
  35. end
  36. -- Unit tests
  37. --[=====[
  38. assert(EscParam("\"abc\"'def''gh") == "'\"abc\"'\\''def'\\'\\''gh'", "fails 1")
  39. assert(EscParam("") == "''", "fails 2")
  40. -- single quotes at the beginning and end could be improved...
  41. assert(EscParam("''abc''") == "''\\'\\''abc'\\'\\'''", "fails 3")
  42. assert(EscParam("\0") == "'\0'", "fails 4")
  43. assert(EscParam("\0xyz\0xyz\0") == "'\0xyz\0xyz\0'", "fails 5")
  44. print("EscParam tests passed")
  45. --]=====]
  46. function FileSystem.GetDirectoryItems(Directory, Options)
  47. Options = Options == nil and {} or Options
  48. Options.Files = Options.Files == nil and true or Options.Files
  49. Options.Directories = Options.Directories == nil and true or Options.Directories
  50. Options.Filter = Options.Filter ~= nil and Options.Filter or OS == "Windows" and "*.*" or "*"
  51. local Cmd = ""
  52. if string.sub(Directory, #Directory, #Directory) ~= FileSystem.Separator() then
  53. Directory = Directory .. FileSystem.Separator()
  54. end
  55. -- TODO: should ShowHidden be a user option?
  56. local ShowHidden = false
  57. if OS == "Windows" then
  58. local HiddenStr = ShowHidden and '' or ' /A:-H'
  59. Directory = string.gsub(Directory, "/", "\\")
  60. if Options.Files and not Options.Directories then
  61. Cmd = 'DIR ' .. EscParam(Directory .. Options.Filter) .. ' /B /A:-D' .. HiddenStr
  62. elseif Options.Directories and not Options.Files then
  63. Cmd = 'DIR ' .. EscParam(Directory) .. ' /B /A:D' .. HiddenStr
  64. else
  65. Cmd = 'DIR ' .. EscParam(Directory) .. ' /B' .. HiddenStr
  66. end
  67. else
  68. local HiddenStr = ShowHidden and "" or " \\( '!' -name .\\* \\)"
  69. if Options.Files and not Options.Directories then
  70. Cmd = 'find ' .. EscParam(Directory) .. " -maxdepth 1 '!' -type d -iname " .. EscParam(Options.Filter) .. HiddenStr .. ' -print0'
  71. elseif Options.Directories and not Options.Files then
  72. Cmd = 'find ' .. EscParam(Directory) .. ' -mindepth 1 -maxdepth 1 -type d' .. HiddenStr .. ' -print0'
  73. else
  74. Cmd = 'find ' .. EscParam(Directory) .. ' -mindepth 1 -maxdepth 1' .. HiddenStr .. ' -print0'
  75. end
  76. end
  77. local Result = {}
  78. local Handle, Error = io.popen(Cmd)
  79. if Handle ~= nil then
  80. local I = 1
  81. local Iterator
  82. if OS == 'Windows' then
  83. Iterator = Handle:lines()
  84. else
  85. Iterator = Handle:read('*all'):gmatch('%Z+')
  86. end
  87. for Item in Iterator do
  88. if Item ~= "nil" or OS ~= "Windows" then
  89. Result[I] = Item
  90. I = I + 1
  91. end
  92. end
  93. io.close(Handle)
  94. end
  95. return Result
  96. end
  97. function FileSystem.Exists(Path)
  98. local Handle = io.open(Path)
  99. if Handle ~= nil then
  100. io.close(Handle)
  101. return true
  102. else
  103. if OS == "Windows" then
  104. local OK, Error, Code = os.rename(Path, Path)
  105. if OK then
  106. return true
  107. else
  108. if Code == 13 then
  109. return true
  110. end
  111. end
  112. end
  113. end
  114. return false
  115. end
  116. function FileSystem.IsDirectory(Path)
  117. return FileSystem.Exists(Path .. FileSystem.Separator())
  118. end
  119. function FileSystem.Parent(Path)
  120. local Result = Path
  121. local Index = 1
  122. local I = Index
  123. repeat
  124. Index = I
  125. I = string.find(Path, FileSystem.Separator(), Index + 1, true)
  126. until I == nil
  127. if Index > 1 then
  128. Result = string.sub(Path, 1, Index - 1)
  129. end
  130. return Result
  131. end
  132. function FileSystem.GetBaseName(Path, RemoveExtension)
  133. local Result = string.match(Path, "^.+/(.+)$")
  134. if Result == nil then
  135. Result = Path
  136. end
  137. if RemoveExtension then
  138. Result = FileSystem.RemoveExtension(Result)
  139. end
  140. return Result
  141. end
  142. function FileSystem.GetDirectory(Path)
  143. local Result = string.match(Path, "(.+)/")
  144. if Result == nil then
  145. Result = Path
  146. end
  147. return Result
  148. end
  149. function FileSystem.GetRootDirectory(Path)
  150. local Result = Path
  151. local Index = string.find(Path, FileSystem.Separator(), 1, true)
  152. if Index ~= nil then
  153. Result = string.sub(Path, 1, Index - 1)
  154. end
  155. return Result
  156. end
  157. function FileSystem.GetSlabPath()
  158. local Path = love.filesystem.getSource()
  159. if not FileSystem.IsDirectory(Path) then
  160. Path = love.filesystem.getSourceBaseDirectory()
  161. end
  162. return Path .. "/Slab"
  163. end
  164. function FileSystem.RemoveExtension(Path)
  165. local Result = string.match(Path, "(.+)%.")
  166. if Result == nil then
  167. Result = Path
  168. end
  169. return Result
  170. end
  171. function FileSystem.ReadContents(Path, IsBinary)
  172. local Result = nil
  173. local Mode = IsBinary and "rb" or "r"
  174. local Handle, Error = io.open(Path, Mode)
  175. if Handle ~= nil then
  176. Result = Handle:read("*a")
  177. Handle:close()
  178. end
  179. return Result, Error
  180. end
  181. function FileSystem.SaveContents(Path, Contents)
  182. local Result = false
  183. local Handle, Error = io.open(Path, "w")
  184. if Handle ~= nil then
  185. Handle:write(Contents)
  186. Handle:close()
  187. Result = true
  188. end
  189. return Result, Error
  190. end
  191. return FileSystem