using that to simplify the tools. Now the tools directly take lists of file paths rather than things that might go through nomsupath or directories or get processed by filetype. Use your shell for globbing stuff like `nomsu tools/test.nom core/*.nom`
147 lines
4.8 KiB
Plaintext
147 lines
4.8 KiB
Plaintext
-- Some file utilities for searching for files recursively and using package.nomsupath
|
|
lpeg = require 'lpeg'
|
|
re = require 're'
|
|
Files = {}
|
|
|
|
run_cmd = (cmd)->
|
|
f = io.popen(cmd)
|
|
lines = [line for line in f\lines!]
|
|
return nil unless f\close!
|
|
return lines
|
|
|
|
_SPOOFED_FILES = {}
|
|
_FILE_CACHE = setmetatable {}, __index:_SPOOFED_FILES
|
|
_BROWSE_CACHE = {}
|
|
|
|
-- Create a fake file and put it in the cache
|
|
_anon_number = 0
|
|
Files.spoof = (filename, contents)->
|
|
if not contents
|
|
filename, contents = "<anonymous file ##{_anon_number}>", filename
|
|
_anon_number += 1
|
|
_SPOOFED_FILES[filename] = contents
|
|
return filename
|
|
|
|
-- Read a file's contents
|
|
Files.read = (filename)->
|
|
if file_contents = _FILE_CACHE[filename]
|
|
return file_contents or nil
|
|
if filename == 'stdin' or filename == '-'
|
|
contents = io.read('*a')
|
|
Files.spoof('stdin', contents)
|
|
Files.spoof('-', contents)
|
|
return contents
|
|
file = io.open(filename)
|
|
return nil unless file
|
|
contents = file\read("*a")
|
|
file\close!
|
|
_FILE_CACHE[filename] = contents
|
|
return contents or nil
|
|
|
|
{:match, :gsub} = string
|
|
|
|
-- TODO: improve sanitization
|
|
sanitize = (path)->
|
|
path = gsub(path,"\\","\\\\")
|
|
path = gsub(path,"`","")
|
|
path = gsub(path,'"','\\"')
|
|
path = gsub(path,"$","")
|
|
return path
|
|
|
|
Files.exists = (path)->
|
|
return true if _SPOOFED_FILES[path]
|
|
return true if path == 'stdin' or path == '-'
|
|
return true if run_cmd("ls #{sanitize(path)}")
|
|
return false
|
|
|
|
Files.list = (path)->
|
|
unless _BROWSE_CACHE[path]
|
|
local files
|
|
_BROWSE_CACHE[path] = if _SPOOFED_FILES[path] or path == 'stdin' or path == '-'
|
|
{path}
|
|
else run_cmd('find -L "'..path..'" -not -path "*/\\.*" -type f') or false
|
|
return _BROWSE_CACHE[path]
|
|
|
|
ok, lfs = pcall(require, "lfs")
|
|
if ok
|
|
raw_file_exists = (filename)->
|
|
mode = lfs.attributes(filename, 'mode')
|
|
return if mode == 'file' or mode == 'directory' or mode == 'link' then true else false
|
|
Files.exists = (path)->
|
|
return true if _SPOOFED_FILES[path]
|
|
return true if path == 'stdin' or path == '-' or raw_file_exists(path)
|
|
return false
|
|
|
|
Files.list = (path)->
|
|
unless _BROWSE_CACHE[path]
|
|
_BROWSE_CACHE[path] = if _SPOOFED_FILES[path] or path == 'stdin' or path == '-'
|
|
{path}
|
|
else
|
|
file_type, err = lfs.attributes(path, 'mode')
|
|
switch file_type
|
|
when "file", "char device"
|
|
{path}
|
|
when "directory", "link"
|
|
files = {}
|
|
for subfile in lfs.dir(path)
|
|
continue if subfile == "." or subfile == ".."
|
|
for f in *(Files.list(path.."/"..subfile) or {})
|
|
files[#files+1] = f
|
|
files
|
|
else false
|
|
-- Filter out any "./" prefix to standardize
|
|
if _BROWSE_CACHE[path]
|
|
for i,f in ipairs(_BROWSE_CACHE[path])
|
|
if f\match("^%./") then _BROWSE_CACHE[path][i] = f\sub(3)
|
|
return _BROWSE_CACHE[path]
|
|
else
|
|
unless run_cmd('find . -maxdepth 0')
|
|
url = if jit
|
|
'https://github.com/spacewander/luafilesystem'
|
|
else 'https://github.com/keplerproject/luafilesystem'
|
|
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`)", 0
|
|
|
|
line_counter = re.compile([[
|
|
lines <- {| line (%nl line)* |}
|
|
line <- {} (!%nl .)*
|
|
]], nl:lpeg.P("\r")^-1 * lpeg.P("\n"))
|
|
|
|
-- LINE_STARTS is a mapping from strings to a table that maps line number to character positions
|
|
_LINE_STARTS = {}
|
|
Files.get_line_starts = (str)->
|
|
if type(str) != 'string'
|
|
str = tostring(str)
|
|
if starts = _LINE_STARTS[str]
|
|
return starts
|
|
line_starts = line_counter\match(str)
|
|
_LINE_STARTS[str] = line_starts
|
|
return line_starts
|
|
|
|
Files.get_line_number = (str, pos)->
|
|
line_starts = Files.get_line_starts(str)
|
|
-- Binary search for line number of position
|
|
lo, hi = 1, #line_starts
|
|
while lo <= hi
|
|
mid = math.floor((lo+hi)/2)
|
|
if line_starts[mid] > pos
|
|
hi = mid-1
|
|
else lo = mid+1
|
|
return hi
|
|
|
|
Files.get_line = (str, line_no)->
|
|
line_starts = Files.get_line_starts(str)
|
|
start = line_starts[line_no]
|
|
return unless start
|
|
stop = line_starts[line_no+1]
|
|
return unless stop
|
|
return (str\sub(start, stop - 2))
|
|
|
|
get_lines = re.compile([[
|
|
lines <- {| line (%nl line)* |}
|
|
line <- {[^%nl]*}
|
|
]], nl:lpeg.P("\r")^-1 * lpeg.P("\n"))
|
|
|
|
Files.get_lines = (str)-> get_lines\match(str)
|
|
|
|
return Files
|