code / nomsu

Lines6.6K Lua5.1K PEG1.3K make117
2 others 83
Markdown60 Bourne Again Shell23
(142 lines)
1 -- Some file utilities for searching for files recursively and using package.nomsupath
2 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 lines
12 _SPOOFED_FILES = {}
13 _BROWSE_CACHE = {}
15 -- Create a fake file and put it in the cache
16 _anon_number = 0
17 Files.spoof = (filename, contents)->
18 if not contents
19 filename, contents = "<anonymous file ##{_anon_number}>", filename
20 _anon_number += 1
21 _SPOOFED_FILES[filename] = contents
22 return filename
24 -- Read a file's contents
25 Files.read = (filename)->
26 if contents = _SPOOFED_FILES[filename]
27 return contents
28 if filename == 'stdin' or filename == '-'
29 contents = io.read('*a')
30 Files.spoof('stdin', contents)
31 Files.spoof('-', contents)
32 return contents
33 file = io.open(filename)
34 return nil unless file
35 contents = file\read("*a")
36 file\close!
37 return contents or nil
39 {:match, :gsub} = string
41 -- TODO: improve sanitization
42 sanitize = (path)->
43 path = gsub(path,"\\","\\\\")
44 path = gsub(path,"`","")
45 path = gsub(path,'"','\\"')
46 path = gsub(path,"$","")
47 return path
49 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 false
55 Files.list = (path)->
56 unless _BROWSE_CACHE[path]
57 local files
58 _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 false
61 return _BROWSE_CACHE[path]
63 Files.make_directory = (path)->
64 run_cmd('mkdir '..path)
66 ok, lfs = pcall(require, "lfs")
67 if ok
68 raw_file_exists = (filename)->
69 mode = lfs.attributes(filename, 'mode')
70 return if mode == 'file' or mode == 'directory' or mode == 'link' then true else false
71 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 false
76 Files.list = (path)->
77 unless _BROWSE_CACHE[path]
78 _BROWSE_CACHE[path] = if _SPOOFED_FILES[path] or path == 'stdin' or path == '-'
79 {path}
80 else
81 file_type, err = lfs.attributes(path, 'mode')
82 switch file_type
83 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] = f
91 files
92 else false
93 -- Filter out any "./" prefix to standardize
94 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.mkdir
100 else
101 unless run_cmd('find . -maxdepth 0')
102 url = if jit
103 '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}", 0
107 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 positions
113 _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 starts
119 line_starts = line_counter\match(str)
120 _LINE_STARTS[str] = line_starts
121 return line_starts
123 Files.get_line_number = (str, pos)->
124 line_starts = Files.get_line_starts(str)
125 -- Binary search for line number of position
126 lo, hi = 1, #line_starts
127 while lo <= hi
128 mid = math.floor((lo+hi)/2)
129 if line_starts[mid] > pos
130 hi = mid-1
131 else lo = mid+1
132 return hi
134 Files.get_line = (str, line_no)->
135 line_starts = Files.get_line_starts(str)
136 start = line_starts[line_no]
137 return unless start
138 stop = line_starts[line_no+1]
139 return unless stop
140 return (str\sub(start, stop - 2))
142 return Files