(142 lines)
1 -- Some file utilities for searching for files recursively and using package.nomsupath2 lpeg = require 'lpeg'3 re = require 're'4 Files = {}6 run_cmd = (cmd)->7 f = io.popen(cmd..' 2>/dev/null')8 lines = [line for line in f\lines!]9 return nil unless f\close!10 return lines12 _SPOOFED_FILES = {}13 _BROWSE_CACHE = {}15 -- Create a fake file and put it in the cache16 _anon_number = 017 Files.spoof = (filename, contents)->18 if not contents19 filename, contents = "<anonymous file ##{_anon_number}>", filename20 _anon_number += 121 _SPOOFED_FILES[filename] = contents22 return filename24 -- Read a file's contents25 Files.read = (filename)->26 if contents = _SPOOFED_FILES[filename]27 return contents28 if filename == 'stdin' or filename == '-'29 contents = io.read('*a')30 Files.spoof('stdin', contents)31 Files.spoof('-', contents)32 return contents33 file = io.open(filename)34 return nil unless file35 contents = file\read("*a")36 file\close!37 return contents or nil39 {:match, :gsub} = string41 -- TODO: improve sanitization42 sanitize = (path)->43 path = gsub(path,"\\","\\\\")44 path = gsub(path,"`","")45 path = gsub(path,'"','\\"')46 path = gsub(path,"$","")47 return path49 Files.exists = (path)->50 return true if _SPOOFED_FILES[path]51 return true if path == 'stdin' or path == '-'52 return true if run_cmd("ls #{sanitize(path)}")53 return false55 Files.list = (path)->56 unless _BROWSE_CACHE[path]57 local files58 _BROWSE_CACHE[path] = if _SPOOFED_FILES[path] or path == 'stdin' or path == '-'59 {path}60 else run_cmd('find -L "'..path..'" -not -path "*/\\.*" -type f') or false61 return _BROWSE_CACHE[path]63 Files.make_directory = (path)->64 run_cmd('mkdir '..path)66 ok, lfs = pcall(require, "lfs")67 if ok68 raw_file_exists = (filename)->69 mode = lfs.attributes(filename, 'mode')70 return if mode == 'file' or mode == 'directory' or mode == 'link' then true else false71 Files.exists = (path)->72 return true if _SPOOFED_FILES[path]73 return true if path == 'stdin' or path == '-' or raw_file_exists(path)74 return false76 Files.list = (path)->77 unless _BROWSE_CACHE[path]78 _BROWSE_CACHE[path] = if _SPOOFED_FILES[path] or path == 'stdin' or path == '-'79 {path}80 else81 file_type, err = lfs.attributes(path, 'mode')82 switch file_type83 when "file", "char device"84 {path}85 when "directory", "link"86 files = {}87 for subfile in lfs.dir(path)88 continue if subfile == "." or subfile == ".."89 for f in *(Files.list(path.."/"..subfile) or {})90 files[#files+1] = f91 files92 else false93 -- Filter out any "./" prefix to standardize94 if _BROWSE_CACHE[path]95 for i,f in ipairs(_BROWSE_CACHE[path])96 if f\match("^%./") then _BROWSE_CACHE[path][i] = f\sub(3)97 return _BROWSE_CACHE[path]99 Files.make_directory = lfs.mkdir100 else101 unless run_cmd('find . -maxdepth 0')102 url = if jit103 'https://github.com/spacewander/luafilesystem'104 else 'https://github.com/keplerproject/luafilesystem'105 error "Could not find 'luafilesystem' module and couldn't run system command `find` (this might happen on Windows). Please install `luafilesystem` (which can be found at: #{url} or `luarocks install luafilesystem`)\n#{lfs}\npackage.cpath: #{package.cpath}", 0107 line_counter = re.compile([[108 lines <- {| line (%nl line)* |}109 line <- {} (!%nl .)*110 ]], nl:lpeg.P("\r")^-1 * lpeg.P("\n"))112 -- LINE_STARTS is a mapping from strings to a table that maps line number to character positions113 _LINE_STARTS = {}114 Files.get_line_starts = (str)->115 if type(str) != 'string'116 str = tostring(str)117 if starts = _LINE_STARTS[str]118 return starts119 line_starts = line_counter\match(str)120 _LINE_STARTS[str] = line_starts121 return line_starts123 Files.get_line_number = (str, pos)->124 line_starts = Files.get_line_starts(str)125 -- Binary search for line number of position126 lo, hi = 1, #line_starts127 while lo <= hi128 mid = math.floor((lo+hi)/2)129 if line_starts[mid] > pos130 hi = mid-1131 else lo = mid+1132 return hi134 Files.get_line = (str, line_no)->135 line_starts = Files.get_line_starts(str)136 start = line_starts[line_no]137 return unless start138 stop = line_starts[line_no+1]139 return unless stop140 return (str\sub(start, stop - 2))142 return Files